clippy-commits Mailing List for Clippy
Status: Pre-Alpha
Brought to you by:
edheldil
You can subscribe to this list here.
| 2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2010 |
Jan
(3) |
Feb
|
Mar
(2) |
Apr
|
May
|
Jun
(2) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|
From: <edh...@us...> - 2010-06-11 22:09:47
|
Revision: 13
http://clippy.svn.sourceforge.net/clippy/?rev=13&view=rev
Author: edheldil
Date: 2010-06-11 21:39:52 +0000 (Fri, 11 Jun 2010)
Log Message:
-----------
Added debug repository for testing network issues
Added Paths:
-----------
clippy/repositories/debug_src_repo.py
Added: clippy/repositories/debug_src_repo.py
===================================================================
--- clippy/repositories/debug_src_repo.py (rev 0)
+++ clippy/repositories/debug_src_repo.py 2010-06-11 21:39:52 UTC (rev 13)
@@ -0,0 +1,86 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+
+import dircache
+import mimetypes
+import os
+import os.path
+import stat
+import string
+
+from clippy import loader, repository
+from clippy.repository import *
+
+
+class DebugSrcRepository (repository.Repository):
+ def __init__ (self, id, config):
+ repository.Repository.__init__ (self, id, config)
+
+ # FIXME: and what about Unicode???
+ #self.fname_trans = string.maketrans ("/\\<>?*`\000", "________")
+
+ # this should work with unicode
+ # FIXME: there are many more, like \001-\037, ...
+ #self.fname_trans = dict (zip (map (ord, u'\a\b\t\n\v\f\r/\\<>?*`'), u'abtnvfr_______'))
+
+
+ @staticmethod
+ def getCaps ():
+ return REPO_REMOTE | REPO_GET
+
+
+ @staticmethod
+ def getConfigParams ():
+ res = repository.Repository.getConfigParams ()
+ res.extend ([
+ ('name', 'Name', 'STR', 'Name ...', 'Debug Source Repository'),
+ ('dir', 'Dir', 'DIR', 'repo dir', os.path.expanduser ('~/clippy_files')),
+ ('read_size', 'Read Size', 'INT', 'Read data chunk size', 4096),
+ ])
+ return res
+
+
+ def getQueryParams (self):
+ # list of tuples: (key, label, type, desc, default)
+ return []
+
+
+ def connect (self):
+ dircache.reset ()
+
+ def getItems (self, query):
+ res = []
+ dir = self.getConfig ('dir')
+ for f in dircache.listdir (dir):
+ id = os.stat (os.path.join (dir, f))[stat.ST_INO]
+ meta = {
+ 'id': str (id),
+ 'name': f,
+ 'filename': f,
+ 'type': mimetypes.guess_type (f)
+ }
+ res.append (meta)
+
+ return res
+
+ def getData (self, metadata):
+ filename = os.path.join (self.getConfig ('dir'), metadata['filename'])
+ fh = open (filename ,'rb')
+ read_size = self.getConfig ("read_size")
+ if read_size:
+ data = ''
+ while True:
+ print ".",
+ chunk = fh.read (read_size)
+ data += chunk
+ if len (chunk) != read_size:
+ break
+ else:
+ data = fh.read ()
+
+ fh.close ()
+
+ return data
+
+
+loader.registerRepository ('debug_src_repo', DebugSrcRepository)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <edh...@us...> - 2010-06-11 21:40:20
|
Revision: 14
http://clippy.svn.sourceforge.net/clippy/?rev=14&view=rev
Author: edheldil
Date: 2010-06-11 21:40:13 +0000 (Fri, 11 Jun 2010)
Log Message:
-----------
Threads
Modified Paths:
--------------
clippy/config.py
clippy/ui/assistant/app.py
clippy/ui/assistant/win_main.py
Modified: clippy/config.py
===================================================================
--- clippy/config.py 2010-06-11 21:39:52 UTC (rev 13)
+++ clippy/config.py 2010-06-11 21:40:13 UTC (rev 14)
@@ -9,7 +9,7 @@
class Config (object):
SYS_CONFIG_FILE = '/etc/clippy.cfg'
CONFIG_DIR = os.path.expanduser ('~/.clippy')
- CONFIG_FILE = os.path.join (CONFIG_DIR, 'clippy.cfg')
+ CONFIG_FILE = os.path.join (CONFIG_DIR, 'config')
USER_REPO_DIR = os.path.join (CONFIG_DIR, 'repositories')
Modified: clippy/ui/assistant/app.py
===================================================================
--- clippy/ui/assistant/app.py 2010-06-11 21:39:52 UTC (rev 13)
+++ clippy/ui/assistant/app.py 2010-06-11 21:40:13 UTC (rev 14)
@@ -48,6 +48,8 @@
self.skip_targets = False
self.skip_sources = False
self.skip_confirm = False
+
+ self.use_threads = True
# FIXME: option for swapping order of source and target pages
self.item_list_mode = 'both' # one of: src, tgt, both
@@ -108,6 +110,13 @@
default = False,
help = "skip download confirmation page")
+ oparser.add_option ("-u",
+ "--no-threads",
+ dest = "use_threads",
+ action = "store_false",
+ default = self.use_threads,
+ help = "don't thread networking")
+
# oparser.add_option ("-o",
# "--option",
# dest = "options",
@@ -130,6 +139,7 @@
self.item_list_mode = opts.item_list_mode
self.skip_confirm = opts.skip_confirm
+ self.use_threads = opts.use_threads
def read_config (self):
self.config.read_file (self.config.CONFIG_FILE, True)
Modified: clippy/ui/assistant/win_main.py
===================================================================
--- clippy/ui/assistant/win_main.py 2010-06-11 21:39:52 UTC (rev 13)
+++ clippy/ui/assistant/win_main.py 2010-06-11 21:40:13 UTC (rev 14)
@@ -6,6 +6,7 @@
import gtk
import pango
import string
+import sys
import thread
import time
@@ -284,7 +285,7 @@
def run_async (self, job_fns, idle_fn, end_fn):
- if True:
+ if app.use_threads:
jobs = [ worker.Job (job_fn) for job_fn in job_fns ]
[ worker.input.put (job) for job in jobs ]
gtk.idle_add (idle_fn, (jobs, end_fn))
@@ -325,9 +326,12 @@
print "Can't get source items:", e
+ self.throbber_phase = 0
+ self.throbber_time = 0
self.run_async ((lambda: app.repo_target.getItems (qry), lambda: app.repo_source.getItems (qry)), self.update_item_list_idle, self.update_item_list_end)
def update_item_list_end (self, results):
+ print
tree = self['tree_items']
items = []
model = self.model_items
@@ -378,7 +382,13 @@
tree.thaw_child_notify ()
def update_item_list_idle (self, data):
- print 'x',
+ pics = "|/-\\"
+ if time.time () - self.throbber_time >0.3:
+ print 3*pics[self.throbber_phase] + "\r",
+ self.throbber_phase = (self.throbber_phase + 1) % len (pics)
+ sys.stdout.flush()
+ self.throbber_time = time.time ()
+
jobs, end_fn = data
if jobs[0].is_done () and jobs[1].is_done ():
end_fn ([ job.result for job in jobs ])
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <edh...@us...> - 2010-03-05 10:08:17
|
Revision: 12
http://clippy.svn.sourceforge.net/clippy/?rev=12&view=rev
Author: edheldil
Date: 2010-03-05 10:08:12 +0000 (Fri, 05 Mar 2010)
Log Message:
-----------
Implemented simple asynchronous execution
Modified Paths:
--------------
clippy/ui/assistant/win_main.py
Added Paths:
-----------
clippy/worker.py
Modified: clippy/ui/assistant/win_main.py
===================================================================
--- clippy/ui/assistant/win_main.py 2010-03-05 10:07:27 UTC (rev 11)
+++ clippy/ui/assistant/win_main.py 2010-03-05 10:08:12 UTC (rev 12)
@@ -6,8 +6,10 @@
import gtk
import pango
import string
+import thread
+import time
-from clippy import loader, repository, utils
+from clippy import loader, repository, utils, worker
from clippy.query import Query
from clippy.ui import glfactory
@@ -281,6 +283,20 @@
self.handle_tree_repos_cursor_changed (tree)
+ def run_async (self, job_fns, idle_fn, end_fn):
+ if True:
+ jobs = [ worker.Job (job_fn) for job_fn in job_fns ]
+ [ worker.input.put (job) for job in jobs ]
+ gtk.idle_add (idle_fn, (jobs, end_fn))
+ #time.sleep (0.001)
+ thread.interrupt_main ()
+ return jobs
+ else:
+ res = [ fn () for fn in job_fns ]
+ end_fn (res)
+ return None
+
+
def update_item_list (self):
tree = self['tree_items']
model = self.model_items
@@ -288,7 +304,6 @@
# FIXME: should also disable sorting
model.clear ()
- items = []
items_src = []
items_tgt = []
@@ -299,9 +314,6 @@
repo = app.repo_target
qry = Query (repo)
repo.connect ()
- items_tgt = repo.getItems (qry)
- for item in items_tgt:
- item['_origin'] = 'tgt'
except Exception, e:
print "Can't get target items:", e
@@ -309,12 +321,24 @@
repo = app.repo_source
qry = Query (repo)
repo.connect ()
- items_src = repo.getItems (qry)
- for item in items_src:
- item['_origin'] = 'src'
except Exception, e:
print "Can't get source items:", e
+
+ self.run_async ((lambda: app.repo_target.getItems (qry), lambda: app.repo_source.getItems (qry)), self.update_item_list_idle, self.update_item_list_end)
+
+ def update_item_list_end (self, results):
+ tree = self['tree_items']
+ items = []
+ model = self.model_items
+ items_tgt = results[0]
+ items_src = results[1]
+ for item in items_tgt:
+ item['_origin'] = 'tgt'
+
+ for item in items_src:
+ item['_origin'] = 'src'
+
# FIXME: this part should be in core
if app.item_list_mode in ['both', 'tgt', 'merge']:
@@ -353,7 +377,16 @@
#self['treecaches'].set_model (self.model_caches)
tree.thaw_child_notify ()
+ def update_item_list_idle (self, data):
+ print 'x',
+ jobs, end_fn = data
+ if jobs[0].is_done () and jobs[1].is_done ():
+ end_fn ([ job.result for job in jobs ])
+ else:
+ gtk.idle_add (self.update_item_list_idle, data)
+ # update throbber
+
def handle_open_metadata (self, *args):
try:
win = app.windows['win_metadata']
Added: clippy/worker.py
===================================================================
--- clippy/worker.py (rev 0)
+++ clippy/worker.py 2010-03-05 10:08:12 UTC (rev 12)
@@ -0,0 +1,36 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+
+import threading
+import Queue
+
+input = Queue.Queue ()
+
+class Worker (threading.Thread):
+ def run (self):
+ while True:
+ job = input.get ()
+ # Empty action causes the worker to terminate
+ if not job.action:
+ break
+ res = job.action ()
+ job.set_result (1, res)
+
+class Job:
+ def __init__ (self, action):
+ self.status = 0
+ self.action = action
+ self.result = None
+
+ def set_result (self, status, result):
+ self.result = result
+ self.status = status
+
+ def is_done (self):
+ return self.status != 0
+
+wrk = Worker ()
+wrk.setDaemon (True)
+wrk.start ()
+
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <edh...@us...> - 2010-03-05 10:07:38
|
Revision: 11
http://clippy.svn.sourceforge.net/clippy/?rev=11&view=rev
Author: edheldil
Date: 2010-03-05 10:07:27 +0000 (Fri, 05 Mar 2010)
Log Message:
-----------
More work on config refactoring
Modified Paths:
--------------
clippy/config.py
clippy/loader.py
clippy/repository.py
clippy/ui/assistant/app.py
clippy/ui/assistant/win_main.py
Modified: clippy/config.py
===================================================================
--- clippy/config.py 2010-01-21 22:30:46 UTC (rev 10)
+++ clippy/config.py 2010-03-05 10:07:27 UTC (rev 11)
@@ -63,7 +63,7 @@
def set (self, key, value):
# Setting unknown keys is not allowed.,keys have to be added first
# FIXME: meaningful exception?
- self.config[key]
+ #self.config[key]
self.config[key] = value
# FIXME: set key in userconfig and make it dirty
Modified: clippy/loader.py
===================================================================
--- clippy/loader.py 2010-01-21 22:30:46 UTC (rev 10)
+++ clippy/loader.py 2010-03-05 10:07:27 UTC (rev 11)
@@ -2,8 +2,13 @@
# vim: set ts=4 sw=4 expandtab:
import os.path
+import sys
+import traceback
+from clippy import config
+from clippy import repository
+
def registerRepository (id, klass):
Loader.registerRepository (id, klass)
@@ -25,6 +30,7 @@
self.loadRepositoryDefinitions (repo_dir, None)
except:
print "Skipping ", repo_dir
+ traceback.print_exc ()
pass
def importRepositoryClasses (self, dir):
@@ -44,13 +50,13 @@
# FIXME: this prevents developer to supply their
# own config class. Rather pass config class factory as an argument to this function
- config = config.Config ()
- config.read (os.path.join (dir, file))
- config = config.config # FIXME: ugly
+ cfg = config.ConfigFile ()
+ cfg.read (os.path.join (dir, file))
+ cfg = cfg.config # FIXME: ugly
- id = config['id']
+ id = cfg['id']
# FIXME: check that id is unique?
- self.registerRepository (id, None, config)
+ self.registerRepository (id, None, cfg)
#yield True
#yield False
@@ -83,7 +89,7 @@
def setupRepositoryClass (self, id):
if not id:
- print >>sys.stderr, "Skipping repo"
+ print >>sys.stderr, "Skipping repo: no id"
return None
klass, config = self.repository_map[id]
@@ -96,15 +102,16 @@
try:
klass_id = config['class']
except KeyError:
- print >>sys.stderr, "Skipping repo"
+ print >>sys.stderr, "Skipping repo: no class"
return None
klass = self.setupRepositoryClass (klass_id)
- if isinstance (Repository, klass):
+ if issubclass (klass, repository.Repository):
self.repository_map[id][0] = klass
return klass
else:
- print >>sys.stderr, "Skipping repo"
+ print "klass:", klass
+ print >>sys.stderr, "Skipping repo: klass no inst"
@@ -118,7 +125,7 @@
self.setupRepositoryConfig (id, id)
base = self.config.get ('repo.' + id + '.class')
if base:
- self.setupRepositoryConfig (app, id, base)
+ self.setupRepositoryConfig (id, base)
def getRepositories (self):
return self.repository_map
Modified: clippy/repository.py
===================================================================
--- clippy/repository.py 2010-01-21 22:30:46 UTC (rev 10)
+++ clippy/repository.py 2010-03-05 10:07:27 UTC (rev 11)
@@ -58,6 +58,11 @@
return self.config.get ('repo.' + self.id + '.' + key)
def setConfig (self, key, value):
+ # FIXME: this is ugly, because the keys are set either
+ # globally, which screws other instances of the class,
+ # or locally, which means that app.config is not
+ # universal and omniscient any more.
+ #self.objconfig.set (key, value)
self.config.set ('repo.' + self.id + '.' + key, value)
def getQueryParams (self):
Modified: clippy/ui/assistant/app.py
===================================================================
--- clippy/ui/assistant/app.py 2010-01-21 22:30:46 UTC (rev 10)
+++ clippy/ui/assistant/app.py 2010-03-05 10:07:27 UTC (rev 11)
@@ -67,7 +67,7 @@
# FIXME: maybe cli args and config should be called first
#print "Init Core"
- self.loader = loader.Loader ()
+ self.loader = loader.Loader (self.config)
self.loader.loadRepositories (utils.USER_REPO_DIR)
# Load Prepare UI for launch
@@ -137,7 +137,7 @@
def setup (self):
self.glade_file = os.path.expanduser (self.config.get ('ui.assistant.glade_file', self.GLADE_FILE))
-
+ self.loader.setupRepositories ()
#print "Loading UI"
self.wtree = gtk.glade.XML (self.glade_file)
@@ -164,7 +164,7 @@
x, y = window.get_position ()
w, h = window.get_size ()
geometry = "%dx%d+%d+%d" %(w, h, x, y)
- self.config.set ('ui.%s.geometry'%window_name, geometry)
+ self.config.set ('ui.assistant.%s.geometry'%window_name, geometry)
# FIXME: account for fullscreen
def progress_fn (self, msg, percent):
@@ -262,7 +262,7 @@
column = gtk.TreeViewColumn ("Value", text_renderer, text = 1)
tree.append_column (column)
- self.restore_geometry (self, 'win_metadata')
+ app.restore_geometry (self, 'win_metadata')
def update (self, meta):
Modified: clippy/ui/assistant/win_main.py
===================================================================
--- clippy/ui/assistant/win_main.py 2010-01-21 22:30:46 UTC (rev 10)
+++ clippy/ui/assistant/win_main.py 2010-03-05 10:07:27 UTC (rev 11)
@@ -270,7 +270,7 @@
for id in app.loader.getRepositories ():
klass, config = app.loader.getRepositoryClass (id)
if klass.getCaps () & caps_mask:
- repo = klass (id, config)
+ repo = app.loader.getRepository (id)
else:
continue
model.append ([repo])
@@ -278,6 +278,7 @@
tree.thaw_child_notify ()
tree.get_selection ().select_path (0)
tree.grab_focus ()
+ self.handle_tree_repos_cursor_changed (tree)
def update_item_list (self):
@@ -312,7 +313,7 @@
for item in items_src:
item['_origin'] = 'src'
except Exception, e:
- print "Can't get target items:", e
+ print "Can't get source items:", e
# FIXME: this part should be in core
@@ -362,6 +363,9 @@
if self.get_current_page () == self.PAGE_ITEMS:
tree = self['tree_items']
+ else:
+ # FIXME: better would be to open repo's metadata window
+ return
selection = tree.get_selection ()
model, iter = selection.get_selected ()
@@ -615,6 +619,7 @@
print "TARGETS"
if app.repo_target is None:
self.update_repo_list (self.PAGE_TARGETS)
+ print app.repo_target
elif page == self.PAGE_SOURCES:
print "SOURCES"
if app.repo_source is None:
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <edh...@us...> - 2010-01-21 22:30:54
|
Revision: 10
http://clippy.svn.sourceforge.net/clippy/?rev=10&view=rev
Author: edheldil
Date: 2010-01-21 22:30:46 +0000 (Thu, 21 Jan 2010)
Log Message:
-----------
Work on refactoring config
Modified Paths:
--------------
clippy/config.py
clippy/loader.py
clippy/repository.py
clippy/ui/assistant/app.py
clippy/ui/shell/app.py
clippy/utils.py
Modified: clippy/config.py
===================================================================
--- clippy/config.py 2010-01-07 23:35:31 UTC (rev 9)
+++ clippy/config.py 2010-01-21 22:30:46 UTC (rev 10)
@@ -19,19 +19,23 @@
self.user_config = None
self.createConfig ()
-
# FIXME: is it a good idea?
#atexit.register (self.write_user_config, self.CONFIG_FILE)
- def read_file (self, filename):
+
+ def read_file (self, filename, user_config=False):
cfg = ConfigFile ()
cfg.read (filename)
self.addConfig (cfg)
+ if user_config:
+ self.user_config = cfg
+
def write_user_config (self):
if self.user_config and self.dirty:
self.user_config.write ()
+
def createConfig (self):
try:
os.mkdir (self.CONFIG_DIR)
@@ -43,8 +47,6 @@
#self.config['ui.win_main.geometry'] = '500x350+20+20'
-
-
def get (self, key, default = None):
# FIXME: get rid of default value?
try:
@@ -65,19 +67,23 @@
self.config[key] = value
# FIXME: set key in userconfig and make it dirty
+
def add (self, key, value):
if not key in self.config:
self.config[key] = value
+
def createKey (self, key, description, default):
self.config[key] = default
+
def addConfig (self, cfg):
if isinstance (cfg, ConfigFile):
cfg = cfg.config
for key, value in cfg.items ():
self.add (key, value)
+
# def getConfigTree (self, prefix):
# if not prefix.endswith ('.'):
# raise ValueError ("Prefix does not end with '.': " + prefix)
Modified: clippy/loader.py
===================================================================
--- clippy/loader.py 2010-01-07 23:35:31 UTC (rev 9)
+++ clippy/loader.py 2010-01-21 22:30:46 UTC (rev 10)
@@ -14,8 +14,8 @@
repository_map = {}
- def __init__ (self):
- pass
+ def __init__ (self, config):
+ self.config = config
def loadRepositories (self, repo_dir):
import clippy.repositories
@@ -50,21 +50,7 @@
id = config['id']
# FIXME: check that id is unique?
- klass = config['class']
- klass = self.getRepositoryClass (klass)
-
- # NOTE: the effect is that config keys defined in main config file
- # shadow those in a repo definition file and in a class.
- #for key, value in config.items ():
- # if key == 'id' or key == 'class':
- # continue
- #
- # key = 'repo.' + id + '.' + key
- # if not key in self.config:
- # #self.config[key] = value
- # pass
-
- self.registerRepository (id, klass, config)
+ self.registerRepository (id, None, config)
#yield True
#yield False
@@ -73,28 +59,73 @@
def registerRepository (id, klass, config = None):
# This method is called from repository modules
- # get repo's config params.
- # merge forward duplicate config keys, i.e. (name, dir, name) becomes (name, dir) (FIXME: just merge?)
- # add config params to global config if they are not there yet
# create instance and give it id and some ref to config
# repo's __init__() sets attrs based on repo.<id>.xxxx
+ if not id in Loader.repository_map:
+ Loader.repository_map[id] = [klass, config]
+ def setupRepositoryConfig (self, id, klass_id):
+ klass, config = self.repository_map[klass_id]
+ if klass is None:
+ return
+
+ if config is not None:
+ for key in config:
+ self.config.add ('repo.' + id + '.' + key, config[key])
+
params = klass.getConfigParams ()
- if config is None:
- config = {}
for param in reversed (params):
key = param[0]
- if not config.has_key (key):
- config[key] = param[4]
+ if key == 'id':
+ continue
+ self.config.add ('repo.' + id + '.' + key, param[4])
- Loader.repository_map[id] = (klass, config)
+ def setupRepositoryClass (self, id):
+ if not id:
+ print >>sys.stderr, "Skipping repo"
+ return None
+
+ klass, config = self.repository_map[id]
+ if klass:
+ return klass
+
+ # Mark this repo as "in progress", to prevent circular dependencies
+ self.repository_map[id][0] = True
+
+ try:
+ klass_id = config['class']
+ except KeyError:
+ print >>sys.stderr, "Skipping repo"
+ return None
+
+ klass = self.setupRepositoryClass (klass_id)
+ if isinstance (Repository, klass):
+ self.repository_map[id][0] = klass
+ return klass
+ else:
+ print >>sys.stderr, "Skipping repo"
+
+
+
+ def setupRepositories (self):
+ for id in self.repository_map:
+ self.setupRepositoryClass (id)
+
+ # FIXME: delete all repos without a class
+
+ for id in self.repository_map:
+ self.setupRepositoryConfig (id, id)
+ base = self.config.get ('repo.' + id + '.class')
+ if base:
+ self.setupRepositoryConfig (app, id, base)
+
def getRepositories (self):
return self.repository_map
def getRepository (self, id):
klass, config = self.getRepositoryClass (id)
- repo = klass (id, config.copy ())
+ repo = klass (id, self.config)
return repo
Modified: clippy/repository.py
===================================================================
--- clippy/repository.py 2010-01-07 23:35:31 UTC (rev 9)
+++ clippy/repository.py 2010-01-21 22:30:46 UTC (rev 10)
@@ -55,10 +55,10 @@
def getConfig (self, key):
# FIXME: recursive search in ancestors
- return self.config[key]
+ return self.config.get ('repo.' + self.id + '.' + key)
def setConfig (self, key, value):
- self.config[key] = value
+ self.config.set ('repo.' + self.id + '.' + key, value)
def getQueryParams (self):
# list of tuples: (key, label, type, desc, default)
Modified: clippy/ui/assistant/app.py
===================================================================
--- clippy/ui/assistant/app.py 2010-01-07 23:35:31 UTC (rev 9)
+++ clippy/ui/assistant/app.py 2010-01-21 22:30:46 UTC (rev 10)
@@ -9,7 +9,7 @@
import gtk
import gtk.glade
-from clippy import loader, utils
+from clippy import loader, utils, config
from clippy.ui import glfactory
from clippy.ui.assistant import win_main
from clippy.ui.assistant import win_repo_config
@@ -20,7 +20,10 @@
#################################################
#################################################
-class ClippyAssistant:
+class ClippyAssistant (object):
+ # NOTE: this would mean that the glade file would have to be with python files, not in /usr/share where it belongs
+ GLADE_FILE = os.path.join (os.path.dirname (__file__) , 'ui.glade')
+
column_hash = {
'name': ( 'Name', 'str'),
'id': ( 'ID', 'str'),
@@ -60,6 +63,8 @@
self.win_main = None
#win_main.app = app
+ self.config = config.Config ()
+
# FIXME: maybe cli args and config should be called first
#print "Init Core"
self.loader = loader.Loader ()
@@ -70,6 +75,7 @@
def main (self):
self.parse_args ()
+ self.read_config ()
self.setup ()
self.run ()
@@ -102,6 +108,13 @@
default = False,
help = "skip download confirmation page")
+# oparser.add_option ("-o",
+# "--option",
+# dest = "options",
+# action = "store_multiple",
+# default = [],
+# help = "set cfg file option")
+
(opts, args) = oparser.parse_args ()
@@ -118,9 +131,12 @@
self.skip_confirm = opts.skip_confirm
+ def read_config (self):
+ self.config.read_file (self.config.CONFIG_FILE, True)
+ self.config.read_file (self.config.SYS_CONFIG_FILE)
def setup (self):
- self.glade_file = os.path.expanduser (self.getConfig ('ui.glade_file'))
+ self.glade_file = os.path.expanduser (self.config.get ('ui.assistant.glade_file', self.GLADE_FILE))
#print "Loading UI"
@@ -137,7 +153,7 @@
gtk.main ()
def restore_geometry (self, window, window_name):
- geometry = self.getConfig ('ui.%s.geometry' %window_name)
+ geometry = self.config.get ('ui.assistant.%s.geometry' %window_name, '')
mo = re.match (r'(\d+)x(\d+)([+-]\d+)([+-]\d+)', geometry)
if mo is not None:
geometry = map (int, mo.group (3, 4, 1, 2))
@@ -148,7 +164,7 @@
x, y = window.get_position ()
w, h = window.get_size ()
geometry = "%dx%d+%d+%d" %(w, h, x, y)
- self.setConfig ('ui.%s.geometry'%window_name, geometry)
+ self.config.set ('ui.%s.geometry'%window_name, geometry)
# FIXME: account for fullscreen
def progress_fn (self, msg, percent):
@@ -167,17 +183,15 @@
# NOTE: this method should be overridden in a child class
- def getConfig (self, key):
- # FIXME: should be in default config?
- cfg = {
- 'ui.glade_file': os.path.join (os.path.dirname (__file__) , 'ui.glade'), # NOTE: glade file has to be with python files, not in /usr/share
- 'ui.win_main.geometry': '500x350+30+30',
- }
-
- return cfg[key]
+# def getConfig (self, key):
+# # FIXME: should be in default config?
+# cfg = {
+# 'ui.glade_file': os.path.join (os.path.dirname (__file__) , 'ui.glade'), # NOTE: glade file has to be with python files, not in /usr/share
+# 'ui.win_main.geometry': '500x350+30+30',
+# }
+#
+# return cfg[key]
- def setConfig (self, key, value):
- pass
# def open_url (self, url):
# if app.browser.find ('%s') >= 0:
Modified: clippy/ui/shell/app.py
===================================================================
--- clippy/ui/shell/app.py 2010-01-07 23:35:31 UTC (rev 9)
+++ clippy/ui/shell/app.py 2010-01-21 22:30:46 UTC (rev 10)
@@ -6,6 +6,7 @@
import readline
import sys
import traceback
+from clippy.config import Config
from clippy.loader import Loader
from clippy.query import Query
import clippy.utils as utils
@@ -14,8 +15,11 @@
HISTORY_FILE = os.path.join (utils.CONFIG_DIR, "cclippy.history")
def __init__ (self):
- self.loader = Loader ()
+ self.config = Config ()
+ # FIXME: read cfg files ...
+ self.loader = Loader (self.config)
self.loader.loadRepositories (utils.USER_REPO_DIR)
+ self.loader.setupRepositories ()
self._readline_init ()
self.encoding = 'ascii'
@@ -150,8 +154,8 @@
for (i, id) in enumerate (sorted (self.loader.getRepositories ())):
# FIXME: this is rather ugly
klass, config = self.loader.getRepositoryClass (id)
- name = config['name']
- desc = config['desc']
+ name = self.config.get ('repo.' + id + '.name')
+ desc = self.config.get ('repo.' + id + '.desc')
print '[%d]\t%s - %s\n\t\t%s' %(i, id, name, desc)
else:
try:
Modified: clippy/utils.py
===================================================================
--- clippy/utils.py 2010-01-07 23:35:31 UTC (rev 9)
+++ clippy/utils.py 2010-01-21 22:30:46 UTC (rev 10)
@@ -9,17 +9,7 @@
CONFIG_FILE = os.path.join (CONFIG_DIR, 'clippy.cfg')
USER_REPO_DIR = os.path.join (CONFIG_DIR, 'repositories')
-if False:
- if config is None:
- self.config = config.Config ()
- else:
- self.config = config
- #self.createConfig ()
- self.config.read (self.CONFIG_FILE)
- # FIXME: is it a good idea?
- atexit.register (self.writeConfigFile, self.CONFIG_FILE)
-
XFER_START = 0
XFER_FILE_START = 1
XFER_FILE_UPDATE = 2
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <edh...@us...> - 2010-01-07 23:35:48
|
Revision: 9
http://clippy.svn.sourceforge.net/clippy/?rev=9&view=rev
Author: edheldil
Date: 2010-01-07 23:35:31 +0000 (Thu, 07 Jan 2010)
Log Message:
-----------
Refactoring repo configuration
Replaced core with repo loader
New repo for art.gnome.org
Simplified assistant ui fiels a bit
Modified Paths:
--------------
Makefile
clippy/config.py
clippy/repositories/dia_shapes_repo.py
clippy/repositories/ghns_repo.py
clippy/repositories/local_dir_repo.py
clippy/repositories/ocal_repo.py
clippy/repositories/rest_repo.py
clippy/repository.py
clippy/ui/__init__.py
clippy/ui/assistant/app.py
clippy/ui/assistant/ui.py
clippy/ui/assistant/win_main.py
clippy/ui/shell/app.py
clippy/utils.py
setup.py
Added Paths:
-----------
clippy/loader.py
clippy/repositories/art_gnome_org_repo.py
Removed Paths:
-------------
clippy/core.py
Modified: Makefile
===================================================================
--- Makefile 2010-01-01 21:14:58 UTC (rev 8)
+++ Makefile 2010-01-07 23:35:31 UTC (rev 9)
@@ -10,7 +10,8 @@
all:
clean:
- -rm *~ *.pyc *.core *.tmp MANIFEST clippy/*~ clippy/*.pyc clippy/*/*~ clippy/*/*.pyc clippy/*/*/*~ clippy/*/*/*.pyc
+ -rm *~ *.pyc *.core *.tmp MANIFEST clippy/*~ clippy/*.pyc clippy/*/*~ clippy/*/*.pyc clippy/*/*/*~ clippy/*/*/*.pyc
+ -rm -rf build tmp
cleanrepo:
-rm ./tout/*
@@ -21,6 +22,9 @@
bdist:
python ./setup.py bdist
+install:
+ python ./setup.py install --prefix=tmp
+
dist:
tar cvzf $(FULLNAME).tar.gz $(SOURCES) $(DATA)
Modified: clippy/config.py
===================================================================
--- clippy/config.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/config.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -15,12 +15,23 @@
def __init__ (self):
self.config = {}
+ self.dirty = False
+ self.user_config = None
self.createConfig ()
+
# FIXME: is it a good idea?
- atexit.register (self.writeConfigFile, self.CONFIG_FILE)
+ #atexit.register (self.write_user_config, self.CONFIG_FILE)
+ def read_file (self, filename):
+ cfg = ConfigFile ()
+ cfg.read (filename)
+ self.addConfig (cfg)
+ def write_user_config (self):
+ if self.user_config and self.dirty:
+ self.user_config.write ()
+
def createConfig (self):
try:
os.mkdir (self.CONFIG_DIR)
@@ -33,44 +44,7 @@
- def read (self, filename, config = None):
- if config is None:
- config = self.config
-
- try:
- fh = open (filename, 'r')
- except IOError:
- return False
- for line in fh:
- line = line.strip ()
- if line == '' or line.startswith ('#'):
- continue
-
- key, value = line.split ('=', 1)
- key = key.strip ()
- value = value.strip ()
-
- config[key] = value
-
- fh.close ()
- return True
-
-
- def write (self, filename):
- try:
- fh = open (filename + '.tmp', 'w')
-
- for key in sorted (self.config):
- fh.write ("%s = %s\n" %(key, str (self.config[key])))
-
- fh.flush ()
- fh.close ()
- os.rename (filename + '.tmp', filename)
- except IOError, e:
- print >>sys.stderr, "Error writing config file %s:" %filename, e
-
-
def get (self, key, default = None):
# FIXME: get rid of default value?
try:
@@ -89,11 +63,20 @@
# FIXME: meaningful exception?
self.config[key]
self.config[key] = value
+ # FIXME: set key in userconfig and make it dirty
+ def add (self, key, value):
+ if not key in self.config:
+ self.config[key] = value
def createKey (self, key, description, default):
self.config[key] = default
+ def addConfig (self, cfg):
+ if isinstance (cfg, ConfigFile):
+ cfg = cfg.config
+ for key, value in cfg.items ():
+ self.add (key, value)
# def getConfigTree (self, prefix):
# if not prefix.endswith ('.'):
@@ -107,3 +90,47 @@
# return res
+class ConfigFile (object):
+ def __init__ (self, **kw):
+ self.config = {}
+ self.filename = None
+
+ def read (self, filename):
+ try:
+ fh = open (filename, 'r')
+ except IOError:
+ return False
+
+ self.filename = filename
+
+ for line in fh:
+ line = line.strip ()
+ if line == '' or line.startswith ('#'):
+ continue
+
+ key, value = line.split ('=', 1)
+ key = key.strip ()
+ value = value.strip ()
+
+ self.config[key] = value
+
+ fh.close ()
+ return True
+
+
+ def write (self, filename=None):
+ if not filename:
+ filename = self.filename
+
+ try:
+ fh = open (filename + '.tmp', 'w')
+
+ for key in sorted (self.config):
+ fh.write ("%s = %s\n" %(key, str (self.config[key])))
+
+ fh.flush ()
+ fh.close ()
+ os.rename (filename + '.tmp', filename)
+ except IOError, e:
+ print >>sys.stderr, "Error writing config file %s:" %filename, e
+
Deleted: clippy/core.py
===================================================================
--- clippy/core.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/core.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -1,105 +0,0 @@
-# -*-python-*-
-# vim: set ts=4 sw=4 expandtab:
-
-import os.path
-
-clippy_core = None
-
-def registerRepository (id, klass):
- clippy_core.registerRepository (id, klass)
-
-
-class Loader (object):
-
- repository_list = []
- repository_map = {}
-
-
- def __init__ (self):
- global clippy_core
- clippy_core = self
-
- def loadRepositories (self, repo_dir):
- import clippy.repositories
- self.importRepositoryClasses (os.path.dirname (clippy.repositories.__file__))
- #self.importRepositoryClasses (self.USER_REPO_DIR)
- try:
- self.loadRepositoryDefinitions (repo_dir, None)
- except:
- print "Skipping ", repo_dir
- pass
-
- def importRepositoryClasses (self, dir):
- for file in sorted (os.listdir (dir)):
- file, ext = os.path.splitext (file)
- if not ext.lower () in ['.py', '.pyc', '.pyo', '.pyw']:
- continue
-
- # FIXME: or use execfile()?
- exec 'import clippy.repositories.' + file
-
-
- def loadRepositoryDefinitions (self, dir, progress_fn):
- for file in sorted (os.listdir (dir)):
- if not file.endswith ('.cfg'):
- continue
-
- # FIXME: this prevents developer to supply their
- # own config class. Rather pass config class factory as an argument to this function
- config = config.Config ()
- config.read (os.path.join (dir, file))
- config = config.config # FIXME: ugly
-
- id = config['id']
- # FIXME: check that id is unique?
- klass = config['class']
- klass = self.getRepositoryClass (klass)
-
- # NOTE: the effect is that config keys defined in main config file
- # shadow those in a repo definition file and in a class.
- #for key, value in config.items ():
- # if key == 'id' or key == 'class':
- # continue
- #
- # key = 'repo.' + id + '.' + key
- # if not key in self.config:
- # #self.config[key] = value
- # pass
-
- self.registerRepository (id, klass, config)
- #yield True
- #yield False
-
-
- def registerRepository (self, id, klass, config = None):
- # This method is called from repository modules
-
- # get repo's config params.
- # merge forward duplicate config keys, i.e. (name, dir, name) becomes (name, dir) (FIXME: just merge?)
- # add config params to global config if they are not there yet
- # create instance and give it id and some ref to config
- # repo's __init__() sets attrs based on repo.<id>.xxxx
-
- params = klass.getConfigParams ()
- if config is None:
- config = {}
- for param in reversed (params):
- key = param[0]
- if not config.has_key (key):
- config[key] = param[4]
-
- Loader.repository_map[id] = (klass, config)
-
- def getRepositories (self):
- return self.repository_map
-
- def getRepository (self, id):
- klass, config = self.getRepositoryClass (id)
- repo = klass (id, config.copy ())
- return repo
-
-
- def getRepositoryClass (self, id):
- return self.repository_map[id]
-
-
Copied: clippy/loader.py (from rev 8, clippy/core.py)
===================================================================
--- clippy/loader.py (rev 0)
+++ clippy/loader.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -0,0 +1,104 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+
+import os.path
+
+
+def registerRepository (id, klass):
+ Loader.registerRepository (id, klass)
+
+
+class Loader (object):
+
+ repository_list = []
+ repository_map = {}
+
+
+ def __init__ (self):
+ pass
+
+ def loadRepositories (self, repo_dir):
+ import clippy.repositories
+ self.importRepositoryClasses (os.path.dirname (clippy.repositories.__file__))
+ #self.importRepositoryClasses (self.USER_REPO_DIR)
+ try:
+ self.loadRepositoryDefinitions (repo_dir, None)
+ except:
+ print "Skipping ", repo_dir
+ pass
+
+ def importRepositoryClasses (self, dir):
+ for file in sorted (os.listdir (dir)):
+ file, ext = os.path.splitext (file)
+ if not ext.lower () in ['.py', '.pyc', '.pyo', '.pyw']:
+ continue
+
+ # FIXME: or use execfile()?
+ exec 'import clippy.repositories.' + file
+
+
+ def loadRepositoryDefinitions (self, dir, progress_fn):
+ for file in sorted (os.listdir (dir)):
+ if not file.endswith ('.cfg'):
+ continue
+
+ # FIXME: this prevents developer to supply their
+ # own config class. Rather pass config class factory as an argument to this function
+ config = config.Config ()
+ config.read (os.path.join (dir, file))
+ config = config.config # FIXME: ugly
+
+ id = config['id']
+ # FIXME: check that id is unique?
+ klass = config['class']
+ klass = self.getRepositoryClass (klass)
+
+ # NOTE: the effect is that config keys defined in main config file
+ # shadow those in a repo definition file and in a class.
+ #for key, value in config.items ():
+ # if key == 'id' or key == 'class':
+ # continue
+ #
+ # key = 'repo.' + id + '.' + key
+ # if not key in self.config:
+ # #self.config[key] = value
+ # pass
+
+ self.registerRepository (id, klass, config)
+ #yield True
+ #yield False
+
+
+ @staticmethod
+ def registerRepository (id, klass, config = None):
+ # This method is called from repository modules
+
+ # get repo's config params.
+ # merge forward duplicate config keys, i.e. (name, dir, name) becomes (name, dir) (FIXME: just merge?)
+ # add config params to global config if they are not there yet
+ # create instance and give it id and some ref to config
+ # repo's __init__() sets attrs based on repo.<id>.xxxx
+
+ params = klass.getConfigParams ()
+ if config is None:
+ config = {}
+ for param in reversed (params):
+ key = param[0]
+ if not config.has_key (key):
+ config[key] = param[4]
+
+ Loader.repository_map[id] = (klass, config)
+
+ def getRepositories (self):
+ return self.repository_map
+
+ def getRepository (self, id):
+ klass, config = self.getRepositoryClass (id)
+ repo = klass (id, config.copy ())
+ return repo
+
+
+ def getRepositoryClass (self, id):
+ return self.repository_map[id]
+
+
Added: clippy/repositories/art_gnome_org_repo.py
===================================================================
--- clippy/repositories/art_gnome_org_repo.py (rev 0)
+++ clippy/repositories/art_gnome_org_repo.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -0,0 +1,184 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+
+import mimetypes
+import os
+import os.path
+import re
+import urllib2
+
+from clippy import loader, repository, query
+from clippy.repository import *
+
+
+class ArtGnomeOrgRepository (repository.Repository):
+ def __init__ (self, id, config):
+ repository.Repository.__init__ (self, id, config)
+ self.base_url = 'http://art.gnome.org/backgrounds'
+ #self.search_url = self.base_url + '/tags'
+
+ #self.link_re = re.compile ('<a href="(' + self.base_url + '/files/([^/]+)/([0-9]+))">([^<]+)</a>')
+
+ #self.dload_re = re.compile ('<p><a href="(' + self.base_url + '/download/[^/]+/([0-9]+))" id="cc_downloadbutton" type="([^"]+)" title="([^"]+)"><span>([^<]*)</span></a>')
+ self.name_re = re.compile ('^<b>([^<>]+)</b>$')
+ self.author_re = re.compile ('^<span class="item-detail">by <script type=.*;</script>([^<>]*)<script type=\'text/javascript\'>document.write')
+ self.license_re = re.compile ('^<br><span class="item-detail"><a href="([^<>"]+)">([^<>]+)</a></span>$')
+ self.preview_re = re.compile ('^<img width="\\d+" height="\\d+" alt="Preview" src=\'(/images/thumbnails/.*)\'>$')
+ self.link_re = re.compile ('^<option value="(/[^<>"]+/(\\d+)/([^<>"/]+))">$')
+ self.type_re = re.compile ('^(\\d+x\\d+|scalable)\\s*</option>$')
+
+ @staticmethod
+ def getCaps ():
+ return REPO_REMOTE | REPO_GET | REPO_HAS_PAGES
+
+ @staticmethod
+ def getConfigParams ():
+ res = repository.Repository.getConfigParams ()
+ res.extend ([
+ ('name', 'Name', 'STR', 'Name...', 'art.Gnome.org (AGO) repository'),
+ ])
+ return res
+
+ def getQueryParams (self):
+ return [
+ #('tag', 'Tag', 'TAG', 'tag desc...', 'backgrounds'),
+ ('page', 'Page', 'NUM', 'page desc...', 0),
+ ]
+
+ def connect (self):
+# username = config['username']
+# password = config['password']
+#
+# auth_handler = urllib2.HTTPBasicAuthHandler ()
+# auth_handler.add_password (
+# realm = self.auth_realm,
+# uri = self.auth_uri,
+# user = username,
+# passwd = password)
+# # ...and install it globally so it can be used with urlopen.
+# opener = urllib2.build_opener (auth_handler)
+# urllib2.install_opener (opener)
+ self.tags = self.getTags ()
+
+ def disconnect (self):
+ self.tags = None
+
+
+ def getTags (self):
+ res = []
+ return res
+ f = urllib2.urlopen (self.search_url)
+ for line in f:
+ if line.find ('class="cc_tag_link"') >= 0:
+ pos = line.index ('>')
+ pos2 = line.index ('<', pos)
+ tag = line[pos+1:pos2]
+ res.append (tag)
+
+ return res
+
+
+ def getItems (self, query):
+ res = []
+
+ url = self.base_url
+ if query.page > 1:
+ url += '?page=' + query.page
+
+ f = urllib2.urlopen (url)
+ line = f.readline ()
+ while line:
+ line = line.strip ()
+
+ if not line.startswith ('<div class="list-item">'):
+ line = f.readline ()
+ continue
+
+ line = f.readline ().strip ()
+ mo = self.name_re.search (line)
+ if not mo:
+ print 'Line does not match line_re:', line
+ continue
+ name = mo.group (1)
+
+ f.readline ()
+
+ line = f.readline ().strip ()
+ mo = self.author_re.search (line)
+ if not mo:
+ print 'Line does not match author_re:', line
+ continue
+ author = mo.group (1)
+
+ line = f.readline ().strip ()
+ mo = self.license_re.search (line)
+ if not mo:
+ print 'Line does not match license_re:', line
+ continue
+ license_url = mo.group (1)
+ license = mo.group (2)
+
+ f.readline ()
+ line = f.readline ().strip ()
+
+ mo = self.preview_re.search (line)
+ if not mo:
+ print 'Line does not match preview_re:', line
+ continue
+
+ preview = mo.group (1)
+
+ f.readline ()
+ f.readline ()
+ f.readline ()
+ f.readline ()
+ f.readline ()
+ f.readline ()
+ line = f.readline ().strip ()
+
+ while line != '</select>':
+ mo = self.link_re.search (line)
+ if not mo:
+ print 'Line does not match link_re:', line
+ break
+
+ url = 'http://art.gnome.org/download?d=' + mo.group (1)
+ id = mo.group (2)
+ filename = mo.group (3)
+
+ line = f.readline ().strip ()
+
+ mo = self.type_re.search (line)
+ if not mo:
+ print 'Line does not match type_re:', line
+ break
+
+ type = mo.group (1)
+
+ meta = {
+ 'id': id,
+ 'name': name,
+ 'filename': filename,
+ 'author': author,
+ 'license': license,
+ 'license_url': license_url,
+ 'type': type,
+ 'preview': 'http://art.gnome.org' + preview,
+ 'url': url,
+ }
+ res.append (meta)
+ line = f.readline ().strip ()
+
+
+ f.close ()
+ return res
+
+
+ def getData (self, metadata):
+ f = urllib2.urlopen (metadata['url'])
+
+ data = f.read ()
+ return data
+
+
+loader.registerRepository ('art_gnome_org_repo', ArtGnomeOrgRepository)
Modified: clippy/repositories/dia_shapes_repo.py
===================================================================
--- clippy/repositories/dia_shapes_repo.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/repositories/dia_shapes_repo.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -8,7 +8,7 @@
import urllib2
from xml.dom.minidom import parse, parseString
-from clippy import core, repository
+from clippy import loader, repository
class DiaShapesRepository (repository.Repository):
@@ -66,4 +66,4 @@
return data
-core.registerRepository ('dia_shapes_repo', DiaShapesRepository)
+loader.registerRepository ('dia_shapes_repo', DiaShapesRepository)
Modified: clippy/repositories/ghns_repo.py
===================================================================
--- clippy/repositories/ghns_repo.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/repositories/ghns_repo.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -7,7 +7,7 @@
import urllib2
from xml.dom.minidom import parse, parseString
-from clippy import core, repository
+from clippy import loader, repository
class GHNSRepository (repository.Repository):
@@ -116,4 +116,4 @@
return data
-core.registerRepository ('ghns_repo', GHNSRepository)
+loader.registerRepository ('ghns_repo', GHNSRepository)
Modified: clippy/repositories/local_dir_repo.py
===================================================================
--- clippy/repositories/local_dir_repo.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/repositories/local_dir_repo.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -7,7 +7,7 @@
import stat
import string
-from clippy import core, repository
+from clippy import loader, repository
from clippy.repository import *
@@ -85,4 +85,4 @@
fh.close ()
-core.registerRepository ('local_dir_repo', LocalDirRepository)
+loader.registerRepository ('local_dir_repo', LocalDirRepository)
Modified: clippy/repositories/ocal_repo.py
===================================================================
--- clippy/repositories/ocal_repo.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/repositories/ocal_repo.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -6,7 +6,7 @@
import re
import urllib2
-from clippy import core, repository, query
+from clippy import loader, repository, query
from clippy.repository import *
@@ -111,4 +111,4 @@
return data
-core.registerRepository ('ocal_repo', OCALRepository)
+loader.registerRepository ('ocal_repo', OCALRepository)
Modified: clippy/repositories/rest_repo.py
===================================================================
--- clippy/repositories/rest_repo.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/repositories/rest_repo.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -5,7 +5,7 @@
import urllib2
from xml.dom.minidom import parse, parseString
-from clippy import core, repository
+from clippy import loader, repository
from clippy.repository import *
@@ -92,4 +92,4 @@
return data
-core.registerRepository ('rest_repo', RestRepository)
+loader.registerRepository ('rest_repo', RestRepository)
Modified: clippy/repository.py
===================================================================
--- clippy/repository.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/repository.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -1,6 +1,5 @@
# -*-python-*-
# vim: set ts=4 sw=4 expandtab:
-from clippy import core
REPO_LOCAL = 0
REPO_CACHE = 0
@@ -23,7 +22,6 @@
class Repository (object):
def __init__ (self, id, config):
- self.core = core.clippy_core
self.id = id
self.config = config
self.name = self.getConfig ('name')
Modified: clippy/ui/__init__.py
===================================================================
--- clippy/ui/__init__.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/ui/__init__.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -1,4 +1,5 @@
# -*-python-*-
-from assistant.ui import ClippyAssistant
+#from shell.ui import ClippyShell
+#from assistant.ui import ClippyAssistant
#from commander.app import ClippyCommander
Modified: clippy/ui/assistant/app.py
===================================================================
--- clippy/ui/assistant/app.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/ui/assistant/app.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -1,22 +1,93 @@
#!/usr/bin/env python
+# vim: set ts=4 sw=4 expandtab:
from optparse import OptionParser
-from clippy.ui import ClippyAssistant
+import os
+import os.path
+import re
-oparser = OptionParser ()
-oparser.add_option ("-s",
+import gtk
+import gtk.glade
+
+from clippy import loader, utils
+from clippy.ui import glfactory
+from clippy.ui.assistant import win_main
+from clippy.ui.assistant import win_repo_config
+
+#################################################
+app = None
+
+#################################################
+#################################################
+
+class ClippyAssistant:
+ column_hash = {
+ 'name': ( 'Name', 'str'),
+ 'id': ( 'ID', 'str'),
+ 'desc': ( 'Desc', 'str'),
+ 'type': ( 'Type', 'str'),
+ 'filename': ( 'Filename', 'str'),
+ 'selection': ('S', 'bool'),
+ }
+
+ COL_HEADER = 0
+ COL_TYPE = 1
+
+ def __init__ (self):
+ global app
+
+ app = self
+ win_main.app = self
+
+ self.repo_source = None
+ self.repo_target = None
+
+ self.skip_targets = False
+ self.skip_sources = False
+ self.skip_confirm = False
+ # FIXME: option for swapping order of source and target pages
+
+ self.item_list_mode = 'both' # one of: src, tgt, both
+
+ self.icon_dir = '.'
+ self.icons = {}
+
+ self.columns = [ 'name', 'desc' ]
+ self.columns_item = { '': [ 'name', 'id', 'type', 'selection', 'filename' ] }
+
+ # Map w/ references to already created windows
+ self.windows = {}
+ self.win_main = None
+ #win_main.app = app
+
+ # FIXME: maybe cli args and config should be called first
+ #print "Init Core"
+ self.loader = loader.Loader ()
+ self.loader.loadRepositories (utils.USER_REPO_DIR)
+
+ # Load Prepare UI for launch
+ ###self.setup ()
+
+ def main (self):
+ self.parse_args ()
+ self.setup ()
+ self.run ()
+
+ def parse_args (self):
+ oparser = OptionParser ()
+ oparser.add_option ("-s",
"--source",
dest = "source",
help = "use source repository REPOID and skip source selection page",
metavar = "REPOID")
-oparser.add_option ("-t",
+ oparser.add_option ("-t",
"--target",
dest = "target",
help = "use target repository REPOID and skip target selection page",
metavar = "REPOID")
-oparser.add_option ("-i",
+ oparser.add_option ("-i",
"--item-list-mode",
dest = "item_list_mode",
help = "item list mode MODE",
@@ -24,28 +95,199 @@
metavar = "MODE")
-oparser.add_option ("-y",
+ oparser.add_option ("-y",
"--skip-confirm",
dest = "skip_confirm",
action = "store_true",
default = False,
help = "skip download confirmation page")
-(opts, args) = oparser.parse_args ()
+ (opts, args) = oparser.parse_args ()
-app = ClippyAssistant ()
-if opts.target is not None:
- r = app.core.getRepository (opts.target)
- app.setTargetRepository (r)
+ if opts.target is not None:
+ r = self.loader.getRepository (opts.target)
+ self.setTargetRepository (r)
-if opts.source is not None:
- r = app.core.getRepository (opts.source)
- app.setSourceRepository (r)
+ if opts.source is not None:
+ r = self.loader.getRepository (opts.source)
+ self.setSourceRepository (r)
-if opts.item_list_mode is not None:
- app.item_list_mode = opts.item_list_mode
+ if opts.item_list_mode is not None:
+ self.item_list_mode = opts.item_list_mode
-app.skip_confirm = opts.skip_confirm
+ self.skip_confirm = opts.skip_confirm
-app.run ()
+
+ def setup (self):
+ self.glade_file = os.path.expanduser (self.getConfig ('ui.glade_file'))
+
+
+ #print "Loading UI"
+ self.wtree = gtk.glade.XML (self.glade_file)
+ self.factory = myFactory (self.wtree)
+
+ #self.icons['XXXX'] = gtk.gdk.pixbuf_new_from_file (os.path.join (app.icon_dir, 'xxxx.png'))
+ self.win_main = self.factory ("win_main")
+ #self.win_main.setup ()
+ self.win_main.show ()
+
+
+ def run (self):
+ gtk.main ()
+
+ def restore_geometry (self, window, window_name):
+ geometry = self.getConfig ('ui.%s.geometry' %window_name)
+ mo = re.match (r'(\d+)x(\d+)([+-]\d+)([+-]\d+)', geometry)
+ if mo is not None:
+ geometry = map (int, mo.group (3, 4, 1, 2))
+ window.resize (geometry[2], geometry[3])
+ window.move (geometry[0], geometry[1])
+
+ def save_geometry (self, window, window_name):
+ x, y = window.get_position ()
+ w, h = window.get_size ()
+ geometry = "%dx%d+%d+%d" %(w, h, x, y)
+ self.setConfig ('ui.%s.geometry'%window_name, geometry)
+ # FIXME: account for fullscreen
+
+ def progress_fn (self, msg, percent):
+ #self.splash_window.update_progress (msg, percent)
+ pass
+
+
+ def setSourceRepository (self, repo):
+ self.repo_source = repo
+ self.skip_sources = True
+
+
+ def setTargetRepository (self, repo):
+ self.repo_target = repo
+ self.skip_targets = True
+
+
+ # NOTE: this method should be overridden in a child class
+ def getConfig (self, key):
+ # FIXME: should be in default config?
+ cfg = {
+ 'ui.glade_file': os.path.join (os.path.dirname (__file__) , 'ui.glade'), # NOTE: glade file has to be with python files, not in /usr/share
+ 'ui.win_main.geometry': '500x350+30+30',
+ }
+
+ return cfg[key]
+
+ def setConfig (self, key, value):
+ pass
+
+# def open_url (self, url):
+# if app.browser.find ('%s') >= 0:
+# os.system (app.browser %url)
+# else:
+# os.system (app.browser + " '" + url + "'")
+#
+#
+# def help (self, topic):
+# self.open_url (app.help_url + '/' + topic + '.html')
+#
+
+# def run_dialog (self, win_name, update_args = (), closers = ()):
+# try: win = self.windows[win_name]
+# except: win = self.windows[win_name] = self.factory (win_name)
+#
+# if callable (update_args):
+# update_args (win)
+# else:
+# try:
+# apply (win.update, update_args)
+# except AttributeError:
+# print "Warning: %s has no update() fn" %win_name
+#
+# while True:
+# res = win.run ()
+# if res in (gtk.RESPONSE_OK, gtk.RESPONSE_CLOSE, gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT) + closers:
+# # FIXME: maybe it would be better to let the caller do win.hide ()
+# win.hide ()
+# return res, win
+#
+# if res == gtk.RESPONSE_HELP:
+# self.help (win_name)
+
+
+#################################################
+#class win_save (glfactory.glObject):
+# def init_widget (self):
+# model = gtk.ListStore (int, str, str)
+# self['treesaveformats'].set_model (model)
+#
+# for fmt in app.formats:
+# model.append (i, fmt[0], fmt[1])
+#
+# renderer = gtk.CellRendererText ()
+#
+# column = gtk.TreeViewColumn ('ext', renderer, text = 1)
+# self['treesaveformats'].append_column (column)
+#
+# column = gtk.TreeViewColumn ('Name', renderer, text = 2)
+# self['treesaveformats'].append_column (column)
+#
+# def on_destroy (self, widget):
+# gtk.main_quit ()
+
+
+#################################################
+class win_metadata (glfactory.glObject):
+ def init_widget (self):
+ self.model = gtk.ListStore (str, str)
+ tree = self['tree_metadata']
+ tree.set_model (self.model)
+
+ text_renderer = gtk.CellRendererText ()
+
+ column = gtk.TreeViewColumn ("Key", text_renderer, text = 0)
+ tree.append_column (column)
+
+ column = gtk.TreeViewColumn ("Value", text_renderer, text = 1)
+ tree.append_column (column)
+ self.restore_geometry (self, 'win_metadata')
+
+
+ def update (self, meta):
+ self.model.clear ()
+ for key in sorted (meta.keys ()):
+ self.model.append ([key, str (meta[key])])
+
+ def on_but_metadata_close_clicked (self, *args):
+ self.hide ()
+
+ def on_delete_event (self, *args):
+ self.hide ()
+ return 1
+
+
+#################################################
+class win_about (glfactory.glObject):
+ def update (self):
+ pass
+
+
+#################################################
+class myFactory (glfactory.glFactory):
+ classes = {
+ 'win_main': win_main.win_main,
+ 'win_metadata': win_metadata,
+ 'win_repo_config': win_repo_config.win_repo_config,
+ 'win_repo_query': win_repo_config.win_repo_query,
+ #'win_about': win_about,
+ }
+
+
+#################################################
+if __name__ == '__main__':
+ # FIXME: need to setup import root to ../..
+ app = ClippyAssistant ()
+ app.main ()
+
+
+#################################################
+# End of file app.py
+
Modified: clippy/ui/assistant/ui.py
===================================================================
--- clippy/ui/assistant/ui.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/ui/assistant/ui.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -9,7 +9,7 @@
import gtk
import gtk.glade
-from clippy import core, utils
+from clippy import loader, utils
from clippy.ui import glfactory
from clippy.ui.assistant import win_main
from clippy.ui.assistant import win_repo_config
@@ -36,8 +36,8 @@
def __init__ (self):
global ui
- ui = self
- win_main.ui = self
+ app = self
+ win_main.app = self
self.repo_source = None
self.repo_target = None
@@ -61,8 +61,8 @@
#win_main.app = app
#print "Init Core"
- self.core = core.Loader ()
- self.core.loadRepositories (utils.USER_REPO_DIR)
+ self.loader = loader.Loader ()
+ self.loader.loadRepositories (utils.USER_REPO_DIR)
# Load Prepare UI for launch
self.setup ()
Modified: clippy/ui/assistant/win_main.py
===================================================================
--- clippy/ui/assistant/win_main.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/ui/assistant/win_main.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -7,13 +7,13 @@
import pango
import string
-from clippy import core, repository
+from clippy import loader, repository, utils
from clippy.query import Query
from clippy.ui import glfactory
#################################################
-ui = None
+app = None
name_lookup = ''
@@ -51,7 +51,7 @@
return ".."
if col == 'selection':
- if ui.win_main.selection.has_key (id (obj)):
+ if app.win_main.selection.has_key (id (obj)):
return True
else:
return False
@@ -71,8 +71,8 @@
if not obj:
return False # FIXME: why do the null objects occur at all????
- if ui.filter:
- for f in ui.filter:
+ if app.filter:
+ for f in app.filter:
if not f (obj): return False
@@ -119,7 +119,7 @@
'on_menu_repo_query_activate': self.handle_open_repo_query,
'on_menu_item_metadata_activate': self.handle_open_metadata,
}
- ui.wtree.signal_autoconnect (signal_dict)
+ app.wtree.signal_autoconnect (signal_dict)
self.set_forward_page_func (self.forward_fn)
self.set_page_complete (self.get_nth_page (self.PAGE_INTRO), True)
@@ -158,7 +158,7 @@
# FIXME: should not be here
self.selection = {}
- ui.restore_geometry (self, 'win_main')
+ app.restore_geometry (self, 'win_main')
def setup_list (self, page):
@@ -166,17 +166,17 @@
coltypes = []
if page == self.PAGE_SOURCES:
- columns += ui.columns
+ columns += app.columns
self.model_sources = model = gtk.ListStore (object)
self.model_sources_filtered = filtered = model.filter_new ()
tree = self['tree_sources']
elif page == self.PAGE_TARGETS:
- columns += ui.columns
+ columns += app.columns
self.model_targets = model = gtk.ListStore (object)
self.model_targets_filtered = filtered = model.filter_new ()
tree = self['tree_targets']
else: # items
- columns += ui.columns_item['']
+ columns += app.columns_item['']
self.model_items = model = gtk.ListStore (object)
self.model_items_filtered = filtered = model.filter_new ()
tree = self['tree_items']
@@ -187,8 +187,8 @@
coltypes.append (object)
continue
- desc = ui.column_hash[c]
- col_type = desc[ui.COL_TYPE]
+ desc = app.column_hash[c]
+ col_type = desc[app.COL_TYPE]
if col_type == 'img':
coltypes.append (gtk.gdk.Pixbuf)
elif col_type == 'bool':
@@ -233,9 +233,9 @@
i = 1
for c in columns[1:]:
- desc = ui.column_hash[c]
- col_label = desc[ui.COL_HEADER]
- col_type = desc[ui.COL_TYPE]
+ desc = app.column_hash[c]
+ col_label = desc[app.COL_HEADER]
+ col_type = desc[app.COL_TYPE]
if col_type == 'img':
column = gtk.TreeViewColumn (col_label, img_renderer, pixbuf = i)
elif col_type == 'bool':
@@ -267,8 +267,8 @@
# FIXME: should also disable sorting
model.clear ()
- for id in ui.core.getRepositories ():
- klass, config = ui.core.getRepositoryClass (id)
+ for id in app.loader.getRepositories ():
+ klass, config = app.loader.getRepositoryClass (id)
if klass.getCaps () & caps_mask:
repo = klass (id, config)
else:
@@ -295,7 +295,7 @@
# FIXME: some list modes do not need source or target items, so do not load them
try:
- repo = ui.repo_target
+ repo = app.repo_target
qry = Query (repo)
repo.connect ()
items_tgt = repo.getItems (qry)
@@ -305,7 +305,7 @@
print "Can't get target items:", e
try:
- repo = ui.repo_source
+ repo = app.repo_source
qry = Query (repo)
repo.connect ()
items_src = repo.getItems (qry)
@@ -316,13 +316,13 @@
# FIXME: this part should be in core
- if ui.item_list_mode in ['both', 'tgt', 'merge']:
+ if app.item_list_mode in ['both', 'tgt', 'merge']:
items.extend (items_tgt)
- if ui.item_list_mode in ['both', 'src', 'merge']:
+ if app.item_list_mode in ['both', 'src', 'merge']:
items.extend (items_src)
- if ui.item_list_mode in ['merge']:
+ if app.item_list_mode in ['merge']:
item_hash = {}
for item in items:
if item_hash.has_key (item['name']):
@@ -334,7 +334,7 @@
items = item_hash.values ()
- if ui.item_list_mode in ['new']:
+ if app.item_list_mode in ['new']:
item_hash = {}
for item in items_tgt:
item_hash[item['name']] = item
@@ -343,7 +343,7 @@
if not item_hash.has_key (item['name']):
items.append (item)
- print "MODE", ui.item_list_mode
+ print "MODE", app.item_list_mode
items = sorted (items, None, lambda item: item['name'])
#self.model_items.append (None)
for item in items:
@@ -355,10 +355,10 @@
def handle_open_metadata (self, *args):
try:
- win = ui.windows['win_metadata']
+ win = app.windows['win_metadata']
except:
- win = ui.factory ('win_metadata')
- ui.windows['win_metadata'] = win
+ win = app.factory ('win_metadata')
+ app.windows['win_metadata'] = win
if self.get_current_page () == self.PAGE_ITEMS:
tree = self['tree_items']
@@ -374,15 +374,15 @@
def handle_open_repo_config (self, *args):
try:
- win = ui.windows['win_repo_config']
+ win = app.windows['win_repo_config']
except:
- win = ui.factory ('win_repo_config')
- ui.windows['win_repo_config'] = win
+ win = app.factory ('win_repo_config')
+ app.windows['win_repo_config'] = win
if self.get_current_page () == self.PAGE_SOURCES:
- repo = ui.repo_source
+ repo = app.repo_source
else:
- repo = ui.repo_target
+ repo = app.repo_target
win.update (repo)
win.show ()
@@ -390,23 +390,23 @@
def handle_open_repo_query (self, *args):
try:
- win = ui.windows['win_repo_query']
+ win = app.windows['win_repo_query']
except:
- win = ui.factory ('win_repo_query')
- ui.windows['win_repo_query'] = win
+ win = app.factory ('win_repo_query')
+ app.windows['win_repo_query'] = win
if self.get_current_page () == self.PAGE_SOURCES:
- repo = ui.repo_source
+ repo = app.repo_source
query = self.query_source
else:
- repo = ui.repo_target
+ repo = app.repo_target
query = self.query_target
win.update (repo, query)
win.show ()
def handle_preferences (self):
- res, win = ui.run_dialog ('win_preferences')
+ res, win = app.run_dialog ('win_preferences')
if res == gtk.RESPONSE_OK:
# FIXME: should we recompute cache.found for the new nickname???
self.update ()
@@ -414,20 +414,20 @@
## win.hide ()
def handle_open_url (self, url):
- if ui.browser.find ('%s') >= 0:
- os.system (ui.browser %url)
+ if app.browser.find ('%s') >= 0:
+ os.system (app.browser %url)
else:
- os.system (ui.browser + " '" + url + "'")
+ os.system (app.browser + " '" + url + "'")
def handle_open_help (self, topic):
- self.handle_open_url (ui.help_url + '/' + topic + '.html')
+ self.handle_open_url (app.help_url + '/' + topic + '.html')
def menu_preferences (self, *args):
self.handle_preferences ()
def menu_about (self, *args):
- res, win = ui.run_dialog ('win_about')
+ res, win = app.run_dialog ('win_about')
@@ -467,11 +467,11 @@
repo = model.get_value (iter, 0)
page = self.get_current_page ()
if page == self.PAGE_TARGETS:
- ui.repo_target = repo
- ui.repo_source = None # do new source repos filter
+ app.repo_target = repo
+ app.repo_source = None # do new source repos filter
self.set_page_complete (self.get_nth_page (self.PAGE_TARGETS), True)
elif page == self.PAGE_SOURCES:
- ui.repo_source = repo
+ app.repo_source = repo
self.selection = None
# FIXME: invalidate items
self.set_page_complete (self.get_nth_page (self.PAGE_SOURCES), True)
@@ -537,7 +537,7 @@
#if s != None:
# app.core.setConfig ('ui.split_position', str (s))
- ui.save_geometry (self, 'win_main')
+ app.save_geometry (self, 'win_main')
def on_tree_sources_button_press_event (self, widget, ev):
@@ -545,7 +545,7 @@
return
if ev.button == 3:
- popup = ui.wtree.get_widget ('menu_repo')
+ popup = app.wtree.get_widget ('menu_repo')
popup.popup (None, None, None, 0, 0)
#elif ev.type == gtk.gdk._2BUTTON_PRESS:
@@ -558,7 +558,7 @@
return
if ev.button == 3:
- popup = ui.wtree.get_widget ('menu_repo')
+ popup = app.wtree.get_widget ('menu_repo')
popup.popup (None, None, None, 0, 0)
#elif ev.type == gtk.gdk._2BUTTON_PRESS:
@@ -570,7 +570,7 @@
return
if ev.button == 3:
- popup = ui.wtree.get_widget ('menu_item')
+ popup = app.wtree.get_widget ('menu_item')
popup.popup (None, None, None, 0, 0)
#elif ev.type == gtk.gdk._2BUTTON_PRESS:
@@ -598,11 +598,11 @@
print "FWD", args
page = args[0]
next = page + 1
- if next == self.PAGE_TARGETS and ui.skip_targets:
+ if next == self.PAGE_TARGETS and app.skip_targets:
next = next + 1
- if next == self.PAGE_SOURCES and ui.skip_sources:
+ if next == self.PAGE_SOURCES and app.skip_sources:
next = next + 1
- if next == self.PAGE_CONFIRM and ui.skip_confirm:
+ if next == self.PAGE_CONFIRM and app.skip_confirm:
next = next + 1
return next
@@ -613,11 +613,11 @@
print "PREPARE" , page
if page == self.PAGE_TARGETS:
print "TARGETS"
- if ui.repo_target is None:
+ if app.repo_target is None:
self.update_repo_list (self.PAGE_TARGETS)
elif page == self.PAGE_SOURCES:
print "SOURCES"
- if ui.repo_source is None:
+ if app.repo_source is None:
self.update_repo_list (self.PAGE_SOURCES)
elif page == self.PAGE_ITEMS:
print "ITEMS"
@@ -633,11 +633,33 @@
print "PROGRESS"
self.log_buf.delete (self.log_buf.get_start_iter (), self.log_buf.get_end_iter ())
#self['vb_page_download'].show ()
- gtk.idle_add (ui.core.xfer (ui.repo_source, ui.repo_target, self.selection, self.progress_fn, self.log_fn).next)
+ gtk.idle_add (utils.xfer (app.repo_source, app.repo_target, self.selection, self.progress_fn).next)
#ui.core.xfer (ui.repo_source, ui.repo_target, self.selection, self.progress_fn, self.log_fn)
- def progress_fn (self, total, total_msg, file, file_msg):
+
+ def progress_fn (self, state, **kw):
+ if state == utils.XFER_START:
+ print "Starting transfer..."
+ elif state == utils.XFER_FILE_START:
+ self['progress_file'].set_fraction (0)
+ self['progress_file'].set_text (kw['name'])
+ self.log_buf.insert (self.log_buf.get_end_iter (), kw['name'] + ' ... ')
+ elif state == utils.XFER_FILE_UPDATE:
+ pass
+ elif state == utils.XFER_FILE_OK:
+ self['progress_file'].set_fraction (1.0)
+ self.log_buf.insert_with_tags_by_name (self.log_buf.get_end_iter (), "OK\n", "ok")
+ elif state == utils.XFER_FILE_ERROR:
+ self.log_buf.insert_with_tags_by_name (self.log_buf.get_end_iter (), "ERROR\n", "error")
+ elif state == utils.XFER_END:
+ print 'Transfer complete'
+ else:
+ pass
+
+ self['text_log'].scroll_to_mark (self.log_buf.get_insert (), 0.05, True, 0.0, 1.0)
+
+ def progressx_fn (self, total, total_msg, file, file_msg):
self['progress_file'].set_fraction (file)
self['progress_file'].set_text (file_msg)
self['progress_total'].set_fraction (total)
Modified: clippy/ui/shell/app.py
===================================================================
--- clippy/ui/shell/app.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/ui/shell/app.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -4,9 +4,9 @@
import atexit
import os.path
import readline
+import sys
import traceback
-
-from clippy.core import Loader
+from clippy.loader import Loader
from clippy.query import Query
import clippy.utils as utils
@@ -14,11 +14,17 @@
HISTORY_FILE = os.path.join (utils.CONFIG_DIR, "cclippy.history")
def __init__ (self):
- self.core = Loader ()
- self.core.loadRepositories (utils.USER_REPO_DIR)
+ self.loader = Loader ()
+ self.loader.loadRepositories (utils.USER_REPO_DIR)
self._readline_init ()
self.encoding = 'ascii'
+ def quit (self):
+ try:
+ readline.write_history_file (self.HISTORY_FILE)
+ except IOError, e:
+ print >>sys.stderr, "W: Can't write history file `%s':" %self.HISTORY_FILE, e
+
def _readline_init (self):
"""Enable history and tab completion for the command line."""
@@ -29,7 +35,7 @@
except IOError:
pass
- atexit.register (readline.write_history_file, self.HISTORY_FILE)
+ atexit.register (self.quit)
###readline.set_pre_input_hook (self._readline_hook)
def _readline_hook ():
@@ -119,7 +125,11 @@
print "Source: ", source_name
print "Target: ", target_name
- cmd = raw_input ('Cmd: ')
+ try:
+ cmd = raw_input ('Cmd: ')
+ except EOFError:
+ print
+ cmd = 'q'
try:
cmd, arg = cmd.split (' ', 1)
@@ -137,21 +147,21 @@
elif cmd == 's' or cmd == 't':
if arg is None:
- for (i, id) in enumerate (sorted (self.core.getRepositories ())):
+ for (i, id) in enumerate (sorted (self.loader.getRepositories ())):
# FIXME: this is rather ugly
- klass, config = self.core.getRepositoryClass (id)
+ klass, config = self.loader.getRepositoryClass (id)
name = config['name']
desc = config['desc']
print '[%d]\t%s - %s\n\t\t%s' %(i, id, name, desc)
else:
try:
arg = int (arg)
- arg = sorted (self.core.getRepositories ())[arg]
+ arg = sorted (self.loader.getRepositories ())[arg]
except:
pass
#try:
- repo = self.core.getRepository (arg)
+ repo = self.loader.getRepository (arg)
#except Exception, e:
# print e
# continue
@@ -245,7 +255,9 @@
else:
print "Unknown command:", cmd
+ print "Quit..."
+
def main ():
app = Shell ()
app.run_shell ()
Modified: clippy/utils.py
===================================================================
--- clippy/utils.py 2010-01-01 21:14:58 UTC (rev 8)
+++ clippy/utils.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -2,6 +2,7 @@
# vim: set ts=4 sw=4 expandtab:
import os.path
+import traceback
SYS_CONFIG_FILE = '/etc/clippy.cfg'
CONFIG_DIR = os.path.expanduser ('~/.clippy')
@@ -44,6 +45,7 @@
target.putData (obj, data)
except Exception, e:
progress_fn (XFER_FILE_ERROR, source=source, target=target, progress=progress, name=obj['name'], error=unicode (e))
+ traceback.print_exc ()
# FIXME: delete t,p file
yield True
continue
Modified: setup.py
===================================================================
--- setup.py 2010-01-01 21:14:58 UTC (rev 8)
+++ setup.py 2010-01-07 23:35:31 UTC (rev 9)
@@ -9,11 +9,13 @@
author = 'Jarda Benkovsky',
author_email = 'edh...@us...',
url = 'http://www.eowyn.cz/clippy',
- scripts = ['cclippy.py'],
- packages = ['clippy', 'clippy.repositories', 'clippy.ui.assistant', 'clippy.ui.commander'],
+ license = 'GPL',
+ scripts = ['cclippy', 'gclippy'],
+ #packages = ['clippy', 'clippy.repositories', 'clippy.ui.shell', 'clippy.ui.assistant', 'clippy.ui.commander'],
+ packages = ['clippy', 'clippy.repositories', 'clippy.ui.shell', 'clippy.ui.assistant'],
data_files = [('share/clippy/assistant', ['clippy/ui/assistant/ui.glade', 'logo.png', 'icon.png']),
('share/clippy/icons', glob.glob ('icons/*.png')),
- ('share/doc/clippy', ['TODO']),
- ('share/doc/gclippy/help', glob.glob ('help/*.html') + glob.glob ('help/*.css') + glob.glob ('help/*.png')),
+ ('share/doc/clippy', ['README', 'TODO']),
+ ('share/doc/clippy/help', glob.glob ('help/*.html') + glob.glob ('help/*.css') + glob.glob ('help/*.png')),
])
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <edh...@us...> - 2010-01-01 21:15:15
|
Revision: 8
http://clippy.svn.sourceforge.net/clippy/?rev=8&view=rev
Author: edheldil
Date: 2010-01-01 21:14:58 +0000 (Fri, 01 Jan 2010)
Log Message:
-----------
Refactored
Replace core with just repo loader
Modified Paths:
--------------
Makefile
clippy/core.py
clippy/repositories/dia_shapes_repo.py
clippy/repositories/ghns_repo.py
clippy/repositories/local_dir_repo.py
clippy/repositories/ocal_repo.py
clippy/repositories/rest_repo.py
clippy/repository.py
clippy/ui/assistant/ui.py
clippy/ui/assistant/win_main.py
Added Paths:
-----------
cclippy
clippy/config.py
clippy/ui/shell/
clippy/ui/shell/__init__.py
clippy/ui/shell/app.py
clippy/utils.py
gclippy
Removed Paths:
-------------
cclippy.py
Modified: Makefile
===================================================================
--- Makefile 2009-05-12 14:33:54 UTC (rev 7)
+++ Makefile 2010-01-01 21:14:58 UTC (rev 8)
@@ -10,8 +10,11 @@
all:
clean:
- -rm *.pyc *.core *.tmp MANIFEST clippy/*.pyc clippy/repositories/*.pyc clippy/ui/*.pyc clippy/ui/*/*.pyc
+ -rm *~ *.pyc *.core *.tmp MANIFEST clippy/*~ clippy/*.pyc clippy/*/*~ clippy/*/*.pyc clippy/*/*/*~ clippy/*/*/*.pyc
+cleanrepo:
+ -rm ./tout/*
+
sdist:
python ./setup.py sdist
Added: cclippy
===================================================================
--- cclippy (rev 0)
+++ cclippy 2010-01-01 21:14:58 UTC (rev 8)
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec python -m clippy.ui.shell.app
Property changes on: cclippy
___________________________________________________________________
Added: svn:executable
+ *
Deleted: cclippy.py
===================================================================
--- cclippy.py 2009-05-12 14:33:54 UTC (rev 7)
+++ cclippy.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -1,221 +0,0 @@
-#!/usr/bin/env python
-
-import atexit
-import os.path
-import readline
-
-from clippy.core import ClippyCore
-from clippy.query import Query
-
-class CClippy (object):
- HISTORY_FILE = os.path.join (ClippyCore.CONFIG_DIR, "cclippy.history")
-
- def __init__ (self):
- self.core = ClippyCore ()
- self._readline_init ()
-
- def run (self):
- repo = self.core.repositories[0]
-
- repo_config = self.core.getConfigTree ('repo.' + repo.id + '.')
-
- repo.connect (repo_config)
- objlist = repo.search (None)
- #for obj in objlist:
- # print obj['id'], obj['name']
-
- data = repo.getObject (objlist[0])
-
- inst.handleData (objlist[0], data)
-
- def _readline_init (self):
- """Enable history and tab completion for the command line."""
-
- readline.parse_and_bind ("tab: complete")
- try:
- readline.read_history_file (self.HISTORY_FILE)
- except IOError:
- pass
-
- atexit.register (readline.write_history_file, self.HISTORY_FILE)
- ###readline.set_pre_input_hook (self._readline_hook)
-
- def _readline_hook ():
- readline.insert_text ('')
- readline.redisplay ()
-
- def help (self):
- print """
-Commands:
- s - set source repository
- t - set target repository
- ls - list source file
- lt - list target files
- qs - list or set source repo query params
- qt - list or set target repo query params
- cs - list or set source repo config params
- ct - list or set target repo config params
- g - get specified ids
- p - put specified ids
- d - delete specified ids
- i - metadata info on specified ids
- apply - transfer previously selected files
- h - this help
- q - quit program
-"""
-
- def run_shell (self):
- source = None
- source_query = None
- target = None
- target_query = None
- items = []
- selection = {}
- connected = False
-
- print "\ntype <?> or <h> for help, <q> for quit\n"
- while True:
- if source is not None:
- source_name = source.name
- else:
- source_name = ''
-
- if target is not None:
- target_name = target.name
- else:
- target_name = ''
-
- print "Source: ", source_name
- print "Target: ", target_name
-
- cmd = raw_input ('Cmd: ')
-
- try:
- cmd, arg = cmd.split (' ', 1)
- except:
- arg = None
-
- if cmd == 'h' or cmd == '?':
- self.help ()
-
- elif cmd == 'q':
- break
-
- elif cmd == 's' or cmd == 't':
- if arg is None:
- for (i, id) in enumerate (sorted (self.core.getRepositories ())):
- # FIXME: this is rather ugly
- name = self.core.getConfig ('repo.' + id + '.name')
- desc = self.core.getConfig ('repo.' + id + '.desc')
- print '[%d]\t%s - %s\n\t\t%s' %(i, id, name, desc)
- else:
- try:
- arg = int (arg)
- arg = sorted (self.core.getRepositories ())[arg]
- except:
- pass
-
- try:
- repo = self.core.getRepository (arg)
- except Exception, e:
- print e
- continue
-
- qry = Query (repo)
- if cmd == 's':
- source = repo
- source_query = qry
- selection = {}
- else:
- target = repo
- target_query = qry
-
- elif cmd == 'ls' or cmd == 'lt':
- if cmd == 'ls':
- repo = source
- qry = source_query
- source.connect ()
- items = source.getItems (source_query)
- else:
- repo = target
- qry = target_query
- target.connect ()
-
- # FIXME: use cache if permitted
- for meta in repo.getItems (qry):
- if meta['id'] in selection:
- sel_sign = '+'
- else:
- sel_sign = ' '
- print sel_sign, meta['id'], meta['name'].encode ('ascii', 'replace')
-
- elif cmd == 'qs' or cmd == 'qt':
- if cmd == 'qs':
- qry = source_query
- else:
- qry = target_query
-
- if arg is None:
- for param in qry.repo.getQueryParams ():
- print param[0], ':', qry.getValue (param[0])
- else:
- key, value = arg.split (' ', 1)
- qry.setValue (key, value)
- # FIXME: getItems ()
-
- elif cmd == 'cs' or cmd == 'ct':
- if cmd == 'cs':
- repo = source
- else:
- repo = target
-
- if arg is None:
- for param in repo.getConfigParams ():
- value = self.core.getConfig ('repo.' + repo.id + '.' + param[0])
- print param[0], ':', value
- else:
- key, value = arg.split (' ', 1)
- self.core.setConfig ('repo.' + repo.id + '.' + key, value)
-
- # FIXME: we have to reload repo now, because the config params
- # are usually only read at class instantiation
-
- # get
- elif cmd == 'g':
- ids = arg.split (' ')
- for id in ids:
- new_sel = filter (lambda m: m['id'] == id, items)
- for m in new_sel:
- selection[m['id']] = m
-
- # info
- elif cmd == 'i':
- ids = arg.split (' ')
- new_sel = filter (lambda m: m['id'] in ids, items)
- for m in new_sel:
- for key, value in m.items ():
- print key + ':', value
-
-
- #put
- elif cmd == 'p':
- pass
-
- # delete
- elif cmd == 'd':
- pass
-
- elif cmd == 'apply':
- ids = selection.keys ()
- ids.sort ()
- for id in ids:
- meta = selection[id]
- print "Getting", id, meta['name'].encode ('ascii', 'replace')
- data = source.getData (meta)
- target.putData (meta, data)
-
- else:
- print "Unknown command:", cmd
-
-app = CClippy ()
-app.run_shell ()
Added: clippy/config.py
===================================================================
--- clippy/config.py (rev 0)
+++ clippy/config.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -0,0 +1,109 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+
+import atexit
+import os.path
+
+
+
+class Config (object):
+ SYS_CONFIG_FILE = '/etc/clippy.cfg'
+ CONFIG_DIR = os.path.expanduser ('~/.clippy')
+ CONFIG_FILE = os.path.join (CONFIG_DIR, 'clippy.cfg')
+ USER_REPO_DIR = os.path.join (CONFIG_DIR, 'repositories')
+
+
+ def __init__ (self):
+ self.config = {}
+ self.createConfig ()
+
+ # FIXME: is it a good idea?
+ atexit.register (self.writeConfigFile, self.CONFIG_FILE)
+
+
+ def createConfig (self):
+ try:
+ os.mkdir (self.CONFIG_DIR)
+ os.mkdir (self.USER_REPO_DIR)
+ except:
+ pass
+
+ # FIXME: see ui.assistant.ui getConfig()
+ #self.config['ui.win_main.geometry'] = '500x350+20+20'
+
+
+
+ def read (self, filename, config = None):
+ if config is None:
+ config = self.config
+
+ try:
+ fh = open (filename, 'r')
+ except IOError:
+ return False
+
+ for line in fh:
+ line = line.strip ()
+ if line == '' or line.startswith ('#'):
+ continue
+
+ key, value = line.split ('=', 1)
+ key = key.strip ()
+ value = value.strip ()
+
+ config[key] = value
+
+ fh.close ()
+ return True
+
+
+ def write (self, filename):
+ try:
+ fh = open (filename + '.tmp', 'w')
+
+ for key in sorted (self.config):
+ fh.write ("%s = %s\n" %(key, str (self.config[key])))
+
+ fh.flush ()
+ fh.close ()
+ os.rename (filename + '.tmp', filename)
+ except IOError, e:
+ print >>sys.stderr, "Error writing config file %s:" %filename, e
+
+
+ def get (self, key, default = None):
+ # FIXME: get rid of default value?
+ try:
+ return self.config[key]
+ except:
+ return default
+
+
+ def getRepoConfig (self, repo, key, default = None):
+ # FIXME: moved to Repository?
+ return self.getConfig ('repo.' + repo.id + '.' + key, default)
+
+
+ def set (self, key, value):
+ # Setting unknown keys is not allowed.,keys have to be added first
+ # FIXME: meaningful exception?
+ self.config[key]
+ self.config[key] = value
+
+
+ def createKey (self, key, description, default):
+ self.config[key] = default
+
+
+# def getConfigTree (self, prefix):
+# if not prefix.endswith ('.'):
+# raise ValueError ("Prefix does not end with '.': " + prefix)
+#
+# res = {}
+# for key, value in self.config.items ():
+# if key.startswith (prefix):
+# res[key[len (prefix):]] = value
+#
+# return res
+
+
Modified: clippy/core.py
===================================================================
--- clippy/core.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/core.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -1,8 +1,7 @@
# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
-import atexit
import os.path
-import urlparse
clippy_core = None
@@ -10,10 +9,7 @@
clippy_core.registerRepository (id, klass)
-class ClippyCore (object):
- CONFIG_DIR = os.path.expanduser ('~/.clippy')
- CONFIG_FILE = os.path.join (CONFIG_DIR, 'clippy.cfg')
- USER_REPO_DIR = os.path.join (CONFIG_DIR, 'repositories')
+class Loader (object):
repository_list = []
repository_map = {}
@@ -23,107 +19,37 @@
global clippy_core
clippy_core = self
- self.config = {}
- try:
- os.mkdir (self.CONFIG_DIR)
- os.mkdir (self.USER_REPO_DIR)
- except:
- pass
-
- self.readConfigFile (self.CONFIG_FILE)
- # FIXME: is it a good idea?
- atexit.register (self.writeConfigFile, self.CONFIG_FILE)
-
+ def loadRepositories (self, repo_dir):
import clippy.repositories
self.importRepositoryClasses (os.path.dirname (clippy.repositories.__file__))
#self.importRepositoryClasses (self.USER_REPO_DIR)
- self.loadRepositoryDefinitions (self.USER_REPO_DIR)
-
-
- def readConfigFile (self, filename, config = None):
- if config is None:
- config = self.config
-
try:
- fh = open (filename, 'r')
- except IOError:
- return False
-
- for line in fh:
- line = line.strip ()
- if line == '' or line.startswith ('#'):
- continue
-
- key, value = line.split ('=', 1)
- key = key.strip ()
- value = value.strip ()
-
- config[key] = value
-
- fh.close ()
- return True
-
-
- def writeConfigFile (self, filename):
- fh = open (filename + '.tmp', 'w')
-
- for key in sorted (self.config):
- fh.write ("%s = %s\n" %(key, str (self.config[key])))
-
- fh.flush ()
- fh.close ()
- os.rename (filename + '.tmp', filename)
-
-
- def getConfig (self, key, default = None):
- # FIXME: get rid of default value?
- try:
- return self.config[key]
+ self.loadRepositoryDefinitions (repo_dir, None)
except:
- return default
+ print "Skipping ", repo_dir
+ pass
-
- def getRepoConfig (self, repo, key, default = None):
- # FIXME: moved to Repository?
- return self.getConfig ('repo.' + repo.id + '.' + key, default)
-
-
- def setConfig (self, key, value):
- # Setting unknown keys is not allowed.,keys have to be added first
- # FIXME: meaningful exception?
- self.config[key]
- self.config[key] = value
-
-
-# def getConfigTree (self, prefix):
-# if not prefix.endswith ('.'):
-# raise ValueError ("Prefix does not end with '.': " + prefix)
-#
-# res = {}
-# for key, value in self.config.items ():
-# if key.startswith (prefix):
-# res[key[len (prefix):]] = value
-#
-# return res
-
-
def importRepositoryClasses (self, dir):
for file in sorted (os.listdir (dir)):
file, ext = os.path.splitext (file)
- if not ext.lower () in ['.py', '.pyc']:
+ if not ext.lower () in ['.py', '.pyc', '.pyo', '.pyw']:
continue
# FIXME: or use execfile()?
exec 'import clippy.repositories.' + file
- def loadRepositoryDefinitions (self, dir):
+ def loadRepositoryDefinitions (self, dir, progress_fn):
for file in sorted (os.listdir (dir)):
if not file.endswith ('.cfg'):
continue
- config = {}
- self.readConfigFile (os.path.join (dir, file), config)
+ # FIXME: this prevents developer to supply their
+ # own config class. Rather pass config class factory as an argument to this function
+ config = config.Config ()
+ config.read (os.path.join (dir, file))
+ config = config.config # FIXME: ugly
+
id = config['id']
# FIXME: check that id is unique?
klass = config['class']
@@ -131,18 +57,21 @@
# NOTE: the effect is that config keys defined in main config file
# shadow those in a repo definition file and in a class.
- for key, value in config.items ():
- if key == 'id' or key == 'class':
- continue
-
- key = 'repo.' + id + '.' + key
- if not key in self.config:
- self.config[key] = value
+ #for key, value in config.items ():
+ # if key == 'id' or key == 'class':
+ # continue
+ #
+ # key = 'repo.' + id + '.' + key
+ # if not key in self.config:
+ # #self.config[key] = value
+ # pass
- self.registerRepository (id, klass)
+ self.registerRepository (id, klass, config)
+ #yield True
+ #yield False
- def registerRepository (self, id, klass):
+ def registerRepository (self, id, klass, config = None):
# This method is called from repository modules
# get repo's config params.
@@ -152,19 +81,21 @@
# repo's __init__() sets attrs based on repo.<id>.xxxx
params = klass.getConfigParams ()
+ if config is None:
+ config = {}
for param in reversed (params):
- key = 'repo.' + id + '.' +param[0]
- if not key in self.config:
- self.config[key] = param[4]
+ key = param[0]
+ if not config.has_key (key):
+ config[key] = param[4]
- ClippyCore.repository_map[id] = klass
+ Loader.repository_map[id] = (klass, config)
def getRepositories (self):
return self.repository_map
def getRepository (self, id):
- klass = self.getRepositoryClass (id)
- repo = klass (id)
+ klass, config = self.getRepositoryClass (id)
+ repo = klass (id, config.copy ())
return repo
@@ -172,30 +103,3 @@
return self.repository_map[id]
- def xfer (self, source, target, selection, progress_fn, log_fn):
- if progress_fn is None:
- progress_fn = lambda a, b, c, d: 0
- if log_fn is None:
- log_fn = lambda a, b: 0
-
- progress_fn (0.0, "", 0.0, "")
- total_files = len (selection)
- for i, obj in enumerate (selection.values ()):
- i = i+ 0.0
- log_fn (0, "Downloading " + obj['name'])
- progress_fn (i/total_files, "", 0.0, obj['name'])
-
- try:
- data = source.getData (obj)
- target.putData (obj, data)
- except Exception, e:
- log_fn (2, "Error\n " +str (e))
- # FIXME: delete t,p file
- yield True
- continue
-
- progress_fn ((i+1)/total_files, "", 1.0, obj['name'])
- log_fn (1, "OK")
- yield True
-
- yield False
Modified: clippy/repositories/dia_shapes_repo.py
===================================================================
--- clippy/repositories/dia_shapes_repo.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/repositories/dia_shapes_repo.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -1,5 +1,6 @@
# -*-python-*-
+import cStringIO
import mimetypes
import os
import os.path
@@ -11,8 +12,8 @@
class DiaShapesRepository (repository.Repository):
- def __init__ (self, id):
- repository.Repository.__init__ (self, id)
+ def __init__ (self, id, config):
+ repository.Repository.__init__ (self, id, config)
self.sheets_url = "http://dia-installer.de/sheets.xml"
@@ -48,7 +49,10 @@
def getItems (self, query):
f = urllib2.urlopen (self.sheets_url)
- dom = parse (f)
+ txt = f.read ()
+ f.close ()
+ mbuf = cStringIO.StringIO (txt)
+ dom = parse (mbuf)
objects = map (self.getNodeMetadata, dom.getElementsByTagName ('sheet'))
dom.unlink ()
f.close ()
Modified: clippy/repositories/ghns_repo.py
===================================================================
--- clippy/repositories/ghns_repo.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/repositories/ghns_repo.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -11,8 +11,8 @@
class GHNSRepository (repository.Repository):
- def __init__ (self, id):
- repository.Repository.__init__ (self, id)
+ def __init__ (self, id, config):
+ repository.Repository.__init__ (self, id, config)
self.category_url = self.getConfig ('category_url')
self.category_re = re.compile ('<td>([^<]+)</td><td>([^<]+)</td><td>([^<]+-providers.xml)</td>')
Modified: clippy/repositories/local_dir_repo.py
===================================================================
--- clippy/repositories/local_dir_repo.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/repositories/local_dir_repo.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -12,9 +12,8 @@
class LocalDirRepository (repository.Repository):
- def __init__ (self, id):
- repository.Repository.__init__ (self, id)
- self.dir = self.getConfig ('dir')
+ def __init__ (self, id, config):
+ repository.Repository.__init__ (self, id, config)
# FIXME: and what about Unicode???
#self.fname_trans = string.maketrans ("/\\<>?*`\000", "________")
@@ -57,8 +56,9 @@
def getItems (self, query):
res = []
- for f in dircache.listdir (self.dir):
- id = os.stat (os.path.join (self.dir, f))[stat.ST_INO]
+ dir = self.getConfig ('dir')
+ for f in dircache.listdir (dir):
+ id = os.stat (os.path.join (dir, f))[stat.ST_INO]
meta = {
'id': str (id),
'name': f,
@@ -70,7 +70,7 @@
return res
def getData (self, metadata):
- filename = os.path.join (self.dir, metadata['filename'])
+ filename = os.path.join (self.getConfig ('dir'), metadata['filename'])
fh = open (filename ,'rb')
data = fh.read ()
fh.close ()
@@ -79,7 +79,7 @@
def putData (self, metadata, data):
# FIXME: check filename for ../, etc
- filename = os.path.join (self.dir, self.cleanFilename (metadata['filename']))
+ filename = os.path.join (self.getConfig ('dir'), self.cleanFilename (metadata['filename']))
fh = open (filename ,'wb')
fh.write (data)
fh.close ()
Modified: clippy/repositories/ocal_repo.py
===================================================================
--- clippy/repositories/ocal_repo.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/repositories/ocal_repo.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -11,8 +11,8 @@
class OCALRepository (repository.Repository):
- def __init__ (self, id):
- repository.Repository.__init__ (self, id)
+ def __init__ (self, id, config):
+ repository.Repository.__init__ (self, id, config)
self.base_url = 'http://openclipart.org/media'
self.search_url = self.base_url + '/tags'
Modified: clippy/repositories/rest_repo.py
===================================================================
--- clippy/repositories/rest_repo.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/repositories/rest_repo.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -10,8 +10,8 @@
class RestRepository (repository.Repository):
- def __init__ (self, id):
- repository.Repository.__init__ (self, id)
+ def __init__ (self, id, config):
+ repository.Repository.__init__ (self, id, config)
self.categories_url = 'http://api.opendesktop.org/v1/content/categories'
self.search_url = 'http://api.opendesktop.org/v1/content/data?categories=1'
self.auth_uri = 'http://api.opendesktop.org/v1/content/'
Modified: clippy/repository.py
===================================================================
--- clippy/repository.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/repository.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -1,5 +1,5 @@
# -*-python-*-
-
+# vim: set ts=4 sw=4 expandtab:
from clippy import core
REPO_LOCAL = 0
@@ -22,18 +22,16 @@
REPO_PARAM_BOOL = 2
class Repository (object):
- def __init__ (self, id):
+ def __init__ (self, id, config):
self.core = core.clippy_core
self.id = id
+ self.config = config
self.name = self.getConfig ('name')
self.desc = self.getConfig ('desc')
self.types = []
- def getConfig (self, key, default = None):
- return self.core.getConfig ('repo.' + self.id + '.' + key, default)
-
@staticmethod
def getCaps ():
return 0
@@ -57,7 +55,13 @@
def getProvidedTypes (self):
return []
+ def getConfig (self, key):
+ # FIXME: recursive search in ancestors
+ return self.config[key]
+ def setConfig (self, key, value):
+ self.config[key] = value
+
def getQueryParams (self):
# list of tuples: (key, label, type, desc, default)
return []
Modified: clippy/ui/assistant/ui.py
===================================================================
--- clippy/ui/assistant/ui.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/ui/assistant/ui.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -1,4 +1,5 @@
# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
#################################################
import os
@@ -8,7 +9,7 @@
import gtk
import gtk.glade
-from clippy import core
+from clippy import core, utils
from clippy.ui import glfactory
from clippy.ui.assistant import win_main
from clippy.ui.assistant import win_repo_config
@@ -60,23 +61,17 @@
#win_main.app = app
#print "Init Core"
- self.core = core.ClippyCore ()
+ self.core = core.Loader ()
+ self.core.loadRepositories (utils.USER_REPO_DIR)
# Load Prepare UI for launch
self.setup ()
def setup (self):
- self.glade_file = os.path.expanduser (self.getConfig ('glade_file'))
+ self.glade_file = os.path.expanduser (self.getConfig ('ui.glade_file'))
- geometry = self.getConfig ('geometry')
- mo = re.match (r'(\d+)x(\d+)([+-]\d+)([+-]\d+)', geometry)
- if mo is not None:
- self.geometry = map (int, mo.group (3, 4, 1, 2))
- else:
- self.geometry = None
-
#print "Loading UI"
self.wtree = gtk.glade.XML (self.glade_file)
self.factory = myFactory (self.wtree)
@@ -90,7 +85,21 @@
def run (self):
gtk.main ()
+ def restore_geometry (self, window, window_name):
+ geometry = self.getConfig ('ui.%s.geometry' %window_name)
+ mo = re.match (r'(\d+)x(\d+)([+-]\d+)([+-]\d+)', geometry)
+ if mo is not None:
+ geometry = map (int, mo.group (3, 4, 1, 2))
+ window.resize (geometry[2], geometry[3])
+ window.move (geometry[0], geometry[1])
+ def save_geometry (self, window, window_name):
+ x, y = window.get_position ()
+ w, h = window.get_size ()
+ geometry = "%dx%d+%d+%d" %(w, h, x, y)
+ self.setConfig ('ui.%s.geometry'%window_name, geometry)
+ # FIXME: account for fullscreen
+
def progress_fn (self, msg, percent):
#self.splash_window.update_progress (msg, percent)
pass
@@ -110,12 +119,14 @@
def getConfig (self, key):
# FIXME: should be in default config?
cfg = {
- 'glade_file': os.path.join (os.path.dirname (__file__) , 'ui.glade'), # NOTE: glade file has to be with python files, not in /usr/share
- 'geometry': '500x350+30+30',
+ 'ui.glade_file': os.path.join (os.path.dirname (__file__) , 'ui.glade'), # NOTE: glade file has to be with python files, not in /usr/share
+ 'ui.win_main.geometry': '500x350+30+30',
}
return cfg[key]
+ def setConfig (self, key, value):
+ pass
# def open_url (self, url):
# if app.browser.find ('%s') >= 0:
@@ -186,6 +197,7 @@
column = gtk.TreeViewColumn ("Value", text_renderer, text = 1)
tree.append_column (column)
+ ui.restore_geometry (self, 'win_metadata')
def update (self, meta):
Modified: clippy/ui/assistant/win_main.py
===================================================================
--- clippy/ui/assistant/win_main.py 2009-05-12 14:33:54 UTC (rev 7)
+++ clippy/ui/assistant/win_main.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -1,4 +1,5 @@
# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
#################################################
import os
@@ -157,9 +158,7 @@
# FIXME: should not be here
self.selection = {}
- if ui.geometry != None:
- self.resize (ui.geometry[2], ui.geometry[3])
- self.move (ui.geometry[0], ui.geometry[1])
+ ui.restore_geometry (self, 'win_main')
def setup_list (self, page):
@@ -268,11 +267,10 @@
# FIXME: should also disable sorting
model.clear ()
-
for id in ui.core.getRepositories ():
- repo_class = ui.core.getRepositoryClass (id)
- if repo_class.getCaps () & caps_mask:
- repo = repo_class (id)
+ klass, config = ui.core.getRepositoryClass (id)
+ if klass.getCaps () & caps_mask:
+ repo = klass (id, config)
else:
continue
model.append ([repo])
@@ -539,11 +537,7 @@
#if s != None:
# app.core.setConfig ('ui.split_position', str (s))
- x, y = self.get_position ()
- w, h = self.get_size ()
- geometry = "%dx%d+%d+%d" %(w, h, x, y)
- ui.core.setConfig ('ui.geometry', geometry)
- # FIXME: account for fullscreen
+ ui.save_geometry (self, 'win_main')
def on_tree_sources_button_press_event (self, widget, ev):
Copied: clippy/ui/shell/app.py (from rev 7, cclippy.py)
===================================================================
--- clippy/ui/shell/app.py (rev 0)
+++ clippy/ui/shell/app.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+# vim: set ts=4 sw=4 expandtab:
+
+import atexit
+import os.path
+import readline
+import traceback
+
+from clippy.core import Loader
+from clippy.query import Query
+import clippy.utils as utils
+
+class Shell (object):
+ HISTORY_FILE = os.path.join (utils.CONFIG_DIR, "cclippy.history")
+
+ def __init__ (self):
+ self.core = Loader ()
+ self.core.loadRepositories (utils.USER_REPO_DIR)
+ self._readline_init ()
+ self.encoding = 'ascii'
+
+
+ def _readline_init (self):
+ """Enable history and tab completion for the command line."""
+
+ readline.parse_and_bind ("tab: complete")
+ try:
+ readline.read_history_file (self.HISTORY_FILE)
+ except IOError:
+ pass
+
+ atexit.register (readline.write_history_file, self.HISTORY_FILE)
+ ###readline.set_pre_input_hook (self._readline_hook)
+
+ def _readline_hook ():
+ readline.insert_text ('')
+ readline.redisplay ()
+
+ def help (self):
+ print """
+Commands:
+ s - set source repository
+ t - set target repository
+ ls - list source file
+ lt - list target files
+ qs - list or set source repo query params
+ qt - list or set target repo query params
+ cs - list or set source repo config params
+ ct - list or set target repo config params
+ g - get specified ids
+ p - put specified ids
+ d - delete specified ids
+ i - metadata info on specified ids
+ apply - transfer previously selected files
+ h - this help
+ q - quit program
+"""
+
+ def list_items (self, repo, qry, selection):
+ try:
+ repo.connect ()
+ items = repo.getItems (qry)
+ except Exception, e:
+ traceback.print_exc ()
+ print
+ return []
+
+
+ # FIXME: use cache if permitted
+ for meta in items:
+ if meta['id'] in selection:
+ sel_sign = '+'
+ else:
+ sel_sign = ' '
+ print sel_sign, meta['id'], meta['name'].encode (self.encoding, 'replace')
+
+ return items
+
+
+ def progress_fn (self, state, **kw):
+ if state == utils.XFER_START:
+ print "Starting transfer..."
+ elif state == utils.XFER_FILE_START:
+ print kw['name'],
+ elif state == utils.XFER_FILE_UPDATE:
+ pass
+ elif state == utils.XFER_FILE_OK:
+ print 'OK'
+ elif state == utils.XFER_FILE_ERROR:
+ print 'ERROR'
+ elif state == utils.XFER_END:
+ print 'Transfer complete'
+ else:
+ pass
+
+
+
+ def run_shell (self):
+ source = None
+ source_query = None
+ target = None
+ target_query = None
+ items = []
+ selection = {}
+ connected = False
+
+ print "\ntype <?> or <h> for help, <q> for quit\n"
+ while True:
+ if source is not None:
+ source_name = source.name
+ else:
+ source_name = ''
+
+ if target is not None:
+ target_name = target.name
+ else:
+ target_name = ''
+
+ print "Source: ", source_name
+ print "Target: ", target_name
+
+ cmd = raw_input ('Cmd: ')
+
+ try:
+ cmd, arg = cmd.split (' ', 1)
+ except:
+ arg = None
+
+ if arg == '':
+ arg = None
+
+ if cmd == 'h' or cmd == '?':
+ self.help ()
+
+ elif cmd == 'q':
+ break
+
+ elif cmd == 's' or cmd == 't':
+ if arg is None:
+ for (i, id) in enumerate (sorted (self.core.getRepositories ())):
+ # FIXME: this is rather ugly
+ klass, config = self.core.getRepositoryClass (id)
+ name = config['name']
+ desc = config['desc']
+ print '[%d]\t%s - %s\n\t\t%s' %(i, id, name, desc)
+ else:
+ try:
+ arg = int (arg)
+ arg = sorted (self.core.getRepositories ())[arg]
+ except:
+ pass
+
+ #try:
+ repo = self.core.getRepository (arg)
+ #except Exception, e:
+ # print e
+ # continue
+
+ qry = Query (repo)
+ if cmd == 's':
+ source = repo
+ source_query = qry
+ selection = {}
+ else:
+ target = repo
+ target_query = qry
+
+ elif cmd == 'ls' or cmd == 'lt':
+ if cmd == 'ls':
+ items = self.list_items (source, source_query, selection)
+ else:
+ self.list_items (target, target_query, selection)
+
+
+ elif cmd == 'qs' or cmd == 'qt':
+ if cmd == 'qs':
+ qry = source_query
+ else:
+ qry = target_query
+
+ if arg is None:
+ for param in qry.repo.getQueryParams ():
+ print param[0], ':', qry.getValue (param[0])
+ else:
+ key, value = arg.split (' ', 1)
+ qry.setValue (key, value)
+ # FIXME: getItems ()
+
+
+ elif cmd == 'cs' or cmd == 'ct':
+ if cmd == 'cs':
+ repo = source
+ else:
+ repo = target
+
+ if arg is None:
+ for param in repo.getConfigParams ():
+ value = repo.getConfig (param[0])
+ print param[0], ':', value
+ else:
+ key, value = arg.split (' ', 1)
+ repo.setConfig (key, value)
+
+ # FIXME: we have to reload repo now, because the config params
+ # are usually only read at class instantiation
+
+ # get
+ elif cmd == 'g':
+ if arg is None or items is None:
+ items = self.list_items (source, source_query, selection)
+
+ else:
+ ids = arg.split (' ')
+ for id in ids:
+ new_sel = filter (lambda m: m['id'] == id, items)
+ for m in new_sel:
+ selection[m['id']] = m
+
+ # info
+ elif cmd == 'i':
+ if arg is None or items is None:
+ items = self.list_items (source, source_query, selection)
+
+ else:
+ ids = arg.split (' ')
+ new_sel = filter (lambda m: m['id'] in ids, items)
+ for m in new_sel:
+ for key, value in m.items ():
+ print key + ':', value
+
+
+ #put
+ elif cmd == 'p':
+ pass
+
+ # delete
+ elif cmd == 'd':
+ pass
+
+ elif cmd == 'apply':
+ gen = utils.xfer (source, target, selection, self.progress_fn)
+ while gen.next ():
+ pass
+
+ else:
+ print "Unknown command:", cmd
+
+
+def main ():
+ app = Shell ()
+ app.run_shell ()
+
+
+if __name__ == '__main__':
+ main ()
+
Added: clippy/utils.py
===================================================================
--- clippy/utils.py (rev 0)
+++ clippy/utils.py 2010-01-01 21:14:58 UTC (rev 8)
@@ -0,0 +1,56 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+
+import os.path
+
+SYS_CONFIG_FILE = '/etc/clippy.cfg'
+CONFIG_DIR = os.path.expanduser ('~/.clippy')
+CONFIG_FILE = os.path.join (CONFIG_DIR, 'clippy.cfg')
+USER_REPO_DIR = os.path.join (CONFIG_DIR, 'repositories')
+
+if False:
+ if config is None:
+ self.config = config.Config ()
+ else:
+ self.config = config
+ #self.createConfig ()
+
+ self.config.read (self.CONFIG_FILE)
+ # FIXME: is it a good idea?
+ atexit.register (self.writeConfigFile, self.CONFIG_FILE)
+
+XFER_START = 0
+XFER_FILE_START = 1
+XFER_FILE_UPDATE = 2
+XFER_FILE_OK = 3
+XFER_FILE_ERROR = 4
+XFER_END = 5
+
+
+def xfer (source, target, selection, progress_fn):
+ if progress_fn is None:
+ progress_fn = lambda s, **kw: 0
+
+ progress_fn (XFER_START, source=source, target=target)
+ files = len (selection)
+ oks = 0
+ errors = 0
+ bytes = 0
+ for i, obj in enumerate (selection.values ()):
+ progress=(i+0.0)/files
+ progress_fn (XFER_FILE_START, source=source, target=target, progress=progress, name=obj['name'])
+ try:
+ data = source.getData (obj)
+ target.putData (obj, data)
+ except Exception, e:
+ progress_fn (XFER_FILE_ERROR, source=source, target=target, progress=progress, name=obj['name'], error=unicode (e))
+ # FIXME: delete t,p file
+ yield True
+ continue
+
+ progress_fn (XFER_FILE_OK, source=source, target=target, progress=(i+1.0)/files, name=obj['name'])
+ # FIXME: remove the tranferred file from selection
+ yield True
+
+ progress_fn (XFER_END)
+ yield False
Added: gclippy
===================================================================
--- gclippy (rev 0)
+++ gclippy 2010-01-01 21:14:58 UTC (rev 8)
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec python -m clippy.ui.assistant.app
Property changes on: gclippy
___________________________________________________________________
Added: svn:executable
+ *
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <edh...@us...> - 2009-05-12 14:34:01
|
Revision: 7
http://clippy.svn.sourceforge.net/clippy/?rev=7&view=rev
Author: edheldil
Date: 2009-05-12 14:33:54 +0000 (Tue, 12 May 2009)
Log Message:
-----------
- Added GTKAssistant client
- too many changes
Modified Paths:
--------------
Makefile
TODO
clippy/core.py
clippy/repositories/ghns_repo.py
clippy/repositories/local_dir_repo.py
clippy/repositories/ocal_repo.py
clippy/repositories/rest_repo.py
clippy/repository.py
setup.py
Added Paths:
-----------
COPYING
README
clippy/repositories/dia_shapes_repo.py
clippy/ui/
clippy/ui/__init__.py
clippy/ui/assistant/
clippy/ui/assistant/__init__.py
clippy/ui/assistant/app.py
clippy/ui/assistant/ui.glade
clippy/ui/assistant/ui.py
clippy/ui/assistant/win_main.py
clippy/ui/assistant/win_repo_config.py
clippy/ui/glfactory.py
Added: COPYING
===================================================================
--- COPYING (rev 0)
+++ COPYING 2009-05-12 14:33:54 UTC (rev 7)
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Modified: Makefile
===================================================================
--- Makefile 2008-11-13 17:18:35 UTC (rev 6)
+++ Makefile 2009-05-12 14:33:54 UTC (rev 7)
@@ -10,7 +10,7 @@
all:
clean:
- -rm *.pyc *.core *.tmp MANIFEST clippy/*.pyc clippy/repositories/*.pyc
+ -rm *.pyc *.core *.tmp MANIFEST clippy/*.pyc clippy/repositories/*.pyc clippy/ui/*.pyc clippy/ui/*/*.pyc
sdist:
python ./setup.py sdist
Added: README
===================================================================
--- README (rev 0)
+++ README 2009-05-12 14:33:54 UTC (rev 7)
@@ -0,0 +1,34 @@
+==== Clippy ====
+
+=== API ===
+
+class ClippyCore
+getRepositoryClass (id)
+getRepository (id)
+getRepositories ()
+xfer (source_repository, target_repository, objects, progress_fn, log_fn)
+
+=== Clippy Assistant API ===
+
+class ClippyAssistant
+
+== Methods ==
+
+setSourceRepository (repository)
+
+setTargetRepository (repository)
+
+run ()
+
+== Attributes ==
+
+core
+ reference to ClippyCore - FIXME: get rid of it, use it as a base class instead
+
+skip_confirm = False
+columns
+columns_item
+TODO: item display mode: source only, source and target mixed, source without target, ...
+TODO: default sort
+
+
Modified: TODO
===================================================================
--- TODO 2008-11-13 17:18:35 UTC (rev 6)
+++ TODO 2009-05-12 14:33:54 UTC (rev 7)
@@ -1,31 +1,74 @@
-- error reporting - exceptions or error codes?
-- how should the insts and repos do i18 stuff? .desktop files?
-- check whether target file does not exist
-- cleanup after failed transfers/installs
-- possibly: the installers should read data directly from repository to their location, to avoid copying files from repoclient's tmp dir to the final destination or spooling large files to memory
-- how to handle archives? e.g. KHotNewStuff has some fonts in zip archives, with README and license file. Probably display the text files when installing?
-- How to handle download pages like in opendesktop.org? Ignore the sucking repo or invoke web browser?
-- should configParams and queryParams be merged?
-- add COPYING and license boilerplates to files - probably LGPL to clippy/*, GPL to cclippy
-- allow searching for recent additions or with the help of page search form (see registry.gimp.org)
-- sorting - GHNS has support for sorting in the *-providers.xml files
+==== Open Issues ====
-Repos to add:
-(sources)
-http://registry.gimp.org/
-http://openfontlibrary.org
-(sinks)
-dia
-blender
-tuxpaint
-gimp
-inkscape
+=== Core, Data Model ===
-Suggested reading:
-GHNS and KNewStuff library http://techbase.kde.org/Development/Tutorials/Introduction_to_Get_Hot_New_Stuff
-http://www.kde-look.org/help/ghns.php
+ * IMPORTANT: rework and finalize the config system:
+ * repos have config in main config and their own config -> confusing
+ * allow to redefine config system by calling app
+
+ * should configParams and queryParams be merged?
+ * add tags to repositories for easy filtering
-OCAL Helper
+=== Repository Handling ===
-eric4 editor's plugin repository
+ * how to handle archives? e.g. KHotNewStuff has some fonts in zip archives, with README and license file. Probably display the text files when installing?
+ * how to handle download pages like in opendesktop.org? Ignore the sucking repo or invoke web browser?
+ * allow searching for recent additions or with the help of page search form (see registry.gimp.org)
+ * sorting - GHNS has support for sorting in the *-providers.xml files
+
+=== File Transfer ===
+
+ * check whether target file does not exist
+ * cleanup after failed transfers/installs
+ * possibly: the installers should read data directly from repository to their location, to avoid copying files from repoclient's tmp dir to the final destination or spooling large files to memory
+ * use threads to download more files at once
+
+==== Repositories ====
+
+ * local_dir_repo- add unzip, un... capability, needs lots of checking for security sake
+
+
+=== Error reporting, debugging, messages ===
+
+ * error reporting - exceptions or error codes?
+ * return list of actions done to calling app
+ * report/debug network problems
+ * centralize message handling
+
+=== Miscellaneous ===
+
+ * double clicking a repository (and hence direct transition to a next page) causes the page back button to behave weirdly
+ * i18n and l15n: How? .desktop files?
+ * add COPYING and license boilerplates to files - probably LGPL to clippy/*, GPL to cclippy
+ * use GTK style file
+ * setup script, compare with existing python projects (gramps, ...)
+ * documentation
+ * homepage
+
+
+
+==== New Repositories ====
+=== Sources ===
+ * http://registry.gimp.org/
+ * http://openfontlibrary.org
+ * http://dia-installer.de/shapes.html
+
+=== Targets ===
+ * dia
+ * blender
+ * tuxpaint
+ * gimp
+ * inkscape
+
+
+
+==== Suggested Reading ====
+
+ * GHNS and KNewStuff library http://techbase.kde.org/Development/Tutorials/Introduction_to_Get_Hot_New_Stuff http://www.kde-look.org/help/ghns.php
+
+ * OCAL Helper
+ * diashapes tool
+ * eric4 editor's plugin repository
+ * gnomesword2 module manager
+ * firefox extension manager/updater
Modified: clippy/core.py
===================================================================
--- clippy/core.py 2008-11-13 17:18:35 UTC (rev 6)
+++ clippy/core.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -2,8 +2,8 @@
import atexit
import os.path
+import urlparse
-
clippy_core = None
def registerRepository (id, klass):
@@ -70,6 +70,7 @@
for key in sorted (self.config):
fh.write ("%s = %s\n" %(key, str (self.config[key])))
+ fh.flush ()
fh.close ()
os.rename (filename + '.tmp', filename)
@@ -161,7 +162,6 @@
def getRepositories (self):
return self.repository_map
-
def getRepository (self, id):
klass = self.getRepositoryClass (id)
repo = klass (id)
@@ -170,3 +170,32 @@
def getRepositoryClass (self, id):
return self.repository_map[id]
+
+
+ def xfer (self, source, target, selection, progress_fn, log_fn):
+ if progress_fn is None:
+ progress_fn = lambda a, b, c, d: 0
+ if log_fn is None:
+ log_fn = lambda a, b: 0
+
+ progress_fn (0.0, "", 0.0, "")
+ total_files = len (selection)
+ for i, obj in enumerate (selection.values ()):
+ i = i+ 0.0
+ log_fn (0, "Downloading " + obj['name'])
+ progress_fn (i/total_files, "", 0.0, obj['name'])
+
+ try:
+ data = source.getData (obj)
+ target.putData (obj, data)
+ except Exception, e:
+ log_fn (2, "Error\n " +str (e))
+ # FIXME: delete t,p file
+ yield True
+ continue
+
+ progress_fn ((i+1)/total_files, "", 1.0, obj['name'])
+ log_fn (1, "OK")
+ yield True
+
+ yield False
Added: clippy/repositories/dia_shapes_repo.py
===================================================================
--- clippy/repositories/dia_shapes_repo.py (rev 0)
+++ clippy/repositories/dia_shapes_repo.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -0,0 +1,65 @@
+# -*-python-*-
+
+import mimetypes
+import os
+import os.path
+import re
+import urllib2
+from xml.dom.minidom import parse, parseString
+
+from clippy import core, repository
+
+
+class DiaShapesRepository (repository.Repository):
+ def __init__ (self, id):
+ repository.Repository.__init__ (self, id)
+ self.sheets_url = "http://dia-installer.de/sheets.xml"
+
+
+ @staticmethod
+ def getCaps ():
+ return repository.REPO_REMOTE | repository.REPO_GET
+
+ @staticmethod
+ def getConfigParams ():
+ res = repository.Repository.getConfigParams ()
+ res.extend ([
+ ('name', 'Name', 'STR', 'Name...', 'Dia Shapes at dia-installer.de repository'),
+ ])
+ return res
+
+ def getQueryParams (self):
+ return [
+ ]
+
+
+ def getNodeMetadata (self, node):
+ metadata = {}
+ metadata['name'] = node.getAttribute ('name')
+ metadata['description'] = node.getAttribute ('descriprion')
+ metadata['creator'] = node.getAttribute ('creator')
+ metadata['website'] = node.getAttribute ('website')
+ metadata['download'] = node.getAttribute ('download')
+
+ metadata['id'] = metadata['name']
+ metadata['filename'] = os.path.basename (metadata['download'])
+ return metadata
+
+
+ def getItems (self, query):
+ f = urllib2.urlopen (self.sheets_url)
+ dom = parse (f)
+ objects = map (self.getNodeMetadata, dom.getElementsByTagName ('sheet'))
+ dom.unlink ()
+ f.close ()
+ return objects
+
+
+ def getData (self, metadata):
+ f = urllib2.urlopen (metadata['download'])
+
+ data = f.read ()
+ return data
+
+
+core.registerRepository ('dia_shapes_repo', DiaShapesRepository)
Modified: clippy/repositories/ghns_repo.py
===================================================================
--- clippy/repositories/ghns_repo.py 2008-11-13 17:18:35 UTC (rev 6)
+++ clippy/repositories/ghns_repo.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -14,22 +14,27 @@
def __init__ (self, id):
repository.Repository.__init__ (self, id)
- self.category_url = 'http://www.kde-look.org/help/ghns.php'
+ self.category_url = self.getConfig ('category_url')
self.category_re = re.compile ('<td>([^<]+)</td><td>([^<]+)</td><td>([^<]+-providers.xml)</td>')
- self.providers_url = 'http://download.kde.org/khotnewstuff/%s-providers.xml'
+ self.providers_url = self.getConfig ('providers_url')
+ self.repo_url = self.getConfig ('repo_url')
+ #self.repo_url = 'http://download.kde.org/khotnewstuff/fonts/fonts.xml'
# if repo_url is not None, do not parse providers.xml file and go to repo_url directly
- #self.repo_url = 'http://download.kde.org/khotnewstuff/fonts/fonts.xml'
- self.repo_url = None
+ #self.repo_url = None
- def getCaps (self):
- return repository.REPO_REMOTE
+ @staticmethod
+ def getCaps ():
+ return repository.REPO_REMOTE | repository.REPO_GET
@staticmethod
def getConfigParams ():
res = repository.Repository.getConfigParams ()
res.extend ([
('name', 'Name', 'STR', 'Name...', 'KDE GetHotNewStuff (GHNS) repository'),
+ ('category_url', 'Category URL', 'STR', 'If empty, use providers file instead', 'http://www.kde-look.org/help/ghns.php'),
+ ('providers_url', 'Providers URL', 'STR', 'If empty, use providers file instead', 'http://download.kde.org/khotnewstuff/%s-providers.xml'),
+ ('repo_url', 'Repository URL', 'STR', 'If empty, use providers file instead', ''),
])
return res
@@ -53,7 +58,10 @@
def connect (self):
- self.categories = self.getCategories ()
+ if self.category_url:
+ self.categories = self.getCategories ()
+ else:
+ self.categories = []
def getNodeText (self, nodelist):
Modified: clippy/repositories/local_dir_repo.py
===================================================================
--- clippy/repositories/local_dir_repo.py 2008-11-13 17:18:35 UTC (rev 6)
+++ clippy/repositories/local_dir_repo.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -24,8 +24,9 @@
self.fname_trans = dict (zip (map (ord, u'\a\b\t\n\v\f\r/\\<>?*`'), u'abtnvfr_______'))
- def getCaps (self):
- return REPO_LOCAL | REPO_DONT_CACHE
+ @staticmethod
+ def getCaps ():
+ return REPO_LOCAL | REPO_DONT_CACHE | REPO_GET | REPO_PUT
@staticmethod
Modified: clippy/repositories/ocal_repo.py
===================================================================
--- clippy/repositories/ocal_repo.py 2008-11-13 17:18:35 UTC (rev 6)
+++ clippy/repositories/ocal_repo.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -23,7 +23,7 @@
@staticmethod
def getCaps ():
- return REPO_REMOTE | REPO_HAS_TAGS | REPO_HAS_PAGES
+ return REPO_REMOTE | REPO_GET | REPO_HAS_TAGS | REPO_HAS_PAGES
@staticmethod
def getConfigParams ():
Modified: clippy/repositories/rest_repo.py
===================================================================
--- clippy/repositories/rest_repo.py 2008-11-13 17:18:35 UTC (rev 6)
+++ clippy/repositories/rest_repo.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -6,6 +6,7 @@
from xml.dom.minidom import parse, parseString
from clippy import core, repository
+from clippy.repository import *
class RestRepository (repository.Repository):
@@ -17,6 +18,10 @@
self.auth_realm = 'your valid user account'
@staticmethod
+ def getCaps ():
+ return REPO_REMOTE | REPO_GET
+
+ @staticmethod
def getConfigParams ():
res = repository.Repository.getConfigParams ()
res.extend ([
Modified: clippy/repository.py
===================================================================
--- clippy/repository.py 2008-11-13 17:18:35 UTC (rev 6)
+++ clippy/repository.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -12,7 +12,15 @@
REPO_HAS_PAGES = 8
REPO_DONT_CACHE = 16
REPO_REQUIRES_AUTH = 32
+REPO_GET = 64
+REPO_PUT = 128
+REPO_DELETE = 256
+# Parametr
+REPO_PARAM_STR = 0
+REPO_PARAM_TAG = 1
+REPO_PARAM_BOOL = 2
+
class Repository (object):
def __init__ (self, id):
self.core = core.clippy_core
@@ -26,7 +34,8 @@
return self.core.getConfig ('repo.' + self.id + '.' + key, default)
- def getCaps (self):
+ @staticmethod
+ def getCaps ():
return 0
@staticmethod
@@ -38,6 +47,17 @@
]
+ # NOTE: these methods can't be static, because different repos of the same class
+ # can support different mime types
+ def getAcceptedTypes (self):
+ # list of tuples (mimetype, encoding, specialtype)
+ # specialtype could be st.like Wallpaper, XMMS-skin, etc.
+ return []
+
+ def getProvidedTypes (self):
+ return []
+
+
def getQueryParams (self):
# list of tuples: (key, label, type, desc, default)
return []
Added: clippy/ui/__init__.py
===================================================================
--- clippy/ui/__init__.py (rev 0)
+++ clippy/ui/__init__.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -0,0 +1,4 @@
+# -*-python-*-
+from assistant.ui import ClippyAssistant
+#from commander.app import ClippyCommander
+
Added: clippy/ui/assistant/app.py
===================================================================
--- clippy/ui/assistant/app.py (rev 0)
+++ clippy/ui/assistant/app.py 2009-05-12 14:33:54 UTC (rev 7)
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+from optparse import OptionParser
+from clippy.ui import ClippyAssistant
+
+oparser = OptionParser ()
+oparser.add_option ("-s",
+ "--source",
+ dest = "source",
+ help = "use source repository REPOID and skip source selection page",
+ metavar = "REPOID")
+
+oparser.add_option ("-t",
+ "--target",
+ dest = "target",
+ help = "use target repository REPOID and skip target selection page",
+ metavar = "REPOID")
+
+oparser.add_option ("-i",
+ "--item-list-mode",
+ dest = "item_list_mode",
+ help = "item list mode MODE",
+ default = "merge",
+ metavar = "MODE")
+
+
+oparser.add_option ("-y",
+ "--skip-confirm",
+ dest = "skip_confirm",
+ action = "store_true",
+ default = False,
+ help = "skip download confirmation page")
+
+(opts, args) = oparser.parse_args ()
+
+app = ClippyAssistant ()
+
+if opts.target is not None:
+ r = app.core.getRepository (opts.target)
+ app.setTargetRepository (r)
+
+if opts.source is not None:
+ r = app.core.getRepository (opts.source)
+ app.setSourceRepository (r)
+
+if opts.item_list_mode is not None:
+ app.item_list_mode = opts.item_list_mode
+
+app.skip_confirm = opts.skip_confirm
+
+app.run ()
Property changes on: clippy/ui/assistant/app.py
___________________________________________________________________
Added: svn:executable
+ *
Added: clippy/ui/assistant/ui.glade
===================================================================
--- clippy/ui/assistant/ui.glade (rev 0)
+++ clippy/ui/assistant/ui.glade 2009-05-12 14:33:54 UTC (rev 7)
@@ -0,0 +1,2448 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Tue May 5 14:51:18 2009 -->
+<glade-interface>
+ <widget class="GtkAssistant" id="win_main">
+ <property name="border_width">10</property>
+ <property name="title" translatable="yes">Clippy</property>
+ <property name="modal">True</property>
+ <child>
+ <widget class="GtkLabel" id="lab_page_intro">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Clippy serves to download ...</property>
+ </widget>
+ <packing>
+ <property name="page_type">GTK_ASSISTANT_PAGE_INTRO</property>
+ <property name="title">Welcome to Clippy</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vb_page_target">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <widget class="GtkTreeView" id="tree_targets">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_clickable">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="title">Select local (target) repository</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vb_page_source">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewport2">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <widget class="GtkTreeView" id="tree_sources">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_clickable">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="title">Select remote (source) repository</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vb_page_items">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewport3">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <widget class="GtkTreeView" id="tree_items">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_clickable">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="but_add">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <child>
+ <widget class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-add</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="but_delete">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <child>
+ <widget class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-delete</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="but_info">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <child>
+ <widget class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="stock">gtk-info</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="title">Set items to download or delete</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vb_page_confirm">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewport4">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <widget class="GtkTextView" id="text_confirm">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="page_type">GTK_ASSISTANT_PAGE_CONFIRM</property>
+ <property name="title">Confirm changes</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vb_page_download">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <widget class="GtkProgressBar" id="progress_file">
+ <property name="visible">True</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>File</b></property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <widget class="GtkProgressBar" id="progress_total">
+ <property name="visible">True</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Total</b></property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewport5">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <widget class="GtkTextView" id="text_log">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="page_type">GTK_ASSISTANT_PAGE_PROGRESS</property>
+ <property name="title">Downloading ...</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/...
[truncated message content] |
|
From: <edh...@us...> - 2008-11-13 17:18:41
|
Revision: 6
http://clippy.svn.sourceforge.net/clippy/?rev=6&view=rev
Author: edheldil
Date: 2008-11-13 17:18:35 +0000 (Thu, 13 Nov 2008)
Log Message:
-----------
Fixed incorrect use of join()
Modified Paths:
--------------
cclippy.py
Modified: cclippy.py
===================================================================
--- cclippy.py 2008-11-13 00:23:50 UTC (rev 5)
+++ cclippy.py 2008-11-13 17:18:35 UTC (rev 6)
@@ -175,8 +175,11 @@
print param[0], ':', value
else:
key, value = arg.split (' ', 1)
- self.core.setConfig ('.'.join ('repo', repo.id, key), value)
+ self.core.setConfig ('repo.' + repo.id + '.' + key, value)
+ # FIXME: we have to reload repo now, because the config params
+ # are usually only read at class instantiation
+
# get
elif cmd == 'g':
ids = arg.split (' ')
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|