Merge lp:~mac9416/keryx/unstable into lp:keryx/stable

Proposed by mac9416
Status: Approved
Approved by: mac9416
Approved revision: 180
Proposed branch: lp:~mac9416/keryx/unstable
Merge into: lp:keryx/stable
Diff against target: 102898 lines (+2394/-99183)
69 files modified
app.fil (+1/-1)
build.bat (+13/-0)
build.sh (+15/-0)
doc/INSTALL (+0/-24)
doc/README (+2/-86)
doc/config.rst (+8/-0)
doc/debian.rst (+5/-0)
doc/index.rst (+32/-0)
doc/keryx.conf (+0/-29)
doc/lib.rst (+8/-0)
doc/log.rst (+8/-0)
doc/package.rst (+8/-0)
doc/plugins.rst (+12/-0)
doc/project.rst (+8/-0)
doc/source.rst (+8/-0)
doc/tutorial.rst (+68/-0)
doc/version.rst (+8/-0)
keryx.py (+29/-110)
keryx.spec (+29/-0)
lib/__init__.py (+90/-29)
lib/config.py (+0/-113)
lib/consts.py (+0/-150)
lib/debian.py (+530/-0)
lib/install.sh (+101/-0)
lib/log.py (+25/-21)
lib/package.py (+43/-0)
lib/plugins.py (+0/-88)
lib/project.py (+95/-85)
lib/source.py (+41/-0)
lib/tests/test_source.py (+14/-0)
lib/version.py (+207/-0)
lib/wxkeryx/__init__.py (+16/-17)
lib/wxkeryx/delayedresult.py (+63/-75)
lib/wxkeryx/download.py (+176/-171)
lib/wxkeryx/editor.py (+17/-15)
lib/wxkeryx/main.py (+306/-284)
lib/wxkeryx/misc.py (+219/-170)
lib/wxkeryx/options.py (+0/-300)
lib/wxkeryx/startDialog.py (+110/-132)
messages.pot (+16/-16)
plugins/ColorMap.py (+0/-66)
plugins/Debian.py (+0/-747)
plugins/Search.py (+0/-148)
projects/hardy-32-bit-default/debian.conf (+0/-5)
projects/hardy-32-bit-default/hardy-32-bit.keryx (+0/-2)
projects/hardy-32-bit-default/lists/status (+0/-15138)
projects/hardy-32-bit-default/sources/sources.list (+0/-52)
projects/hardy-64-bit-default/debian.conf (+0/-5)
projects/hardy-64-bit-default/hardy-64-bit.keryx (+0/-2)
projects/hardy-64-bit-default/lists/status (+0/-14740)
projects/hardy-64-bit-default/sources/sources.list (+0/-52)
projects/jaunty-32-bit-default/debian.conf (+0/-5)
projects/jaunty-32-bit-default/jaunty-32-bit.keryx (+0/-2)
projects/jaunty-32-bit-default/lists/status (+0/-15966)
projects/jaunty-32-bit-default/sources/sources.list (+0/-53)
projects/jaunty-64-bit-default/debian.conf (+0/-5)
projects/jaunty-64-bit-default/jaunty-64-bit.keryx (+0/-2)
projects/jaunty-64-bit-default/lists/status (+0/-16924)
projects/jaunty-64-bit-default/sources/sources.list (+0/-53)
projects/karmic-32-bit-default/debian.conf (+0/-5)
projects/karmic-32-bit-default/karmic-32-bit.keryx (+0/-2)
projects/karmic-32-bit-default/lists/status (+0/-16622)
projects/karmic-32-bit-default/sources/sources.list (+0/-52)
projects/karmic-64-bit-default/debian.conf (+0/-5)
projects/karmic-64-bit-default/karmic-64-bit.keryx (+0/-2)
projects/karmic-64-bit-default/lists/status (+0/-16504)
projects/karmic-64-bit-default/sources/sources.list (+0/-52)
requirements.txt (+1/-0)
setup.py (+62/-56)
To merge this branch: bzr merge lp:~mac9416/keryx/unstable
Reviewer Review Type Date Requested Status
mac9416 Approve
Review via email: mp+325135@code.launchpad.net

Description of the change

This is a not-too-beautiful refactoring of 0.92.4. But it's functional.

To post a comment you must log in.
Revision history for this message
mac9416 (mac9416) :
review: Approve
lp:~mac9416/keryx/unstable updated
181. By mac9416

Updated spec, added missing lib file.

182. By mac9416

Final changes for 0.92.5 release

183. By mac9416

But seriously, last change for 0.92.5

Unmerged revisions

183. By mac9416

But seriously, last change for 0.92.5

182. By mac9416

Final changes for 0.92.5 release

181. By mac9416

Updated spec, added missing lib file.

180. By mac9416

release 0.92.5

179. By mac9416

Cleaned up the start dialog

178. By mac9416

Removed default projects

177. By mac9416

Got rid of keryx.conf. Convention > Configuration.

176. By mac9416

Added tools to build a Linux executable.

175. By mac9416

Clean up the Debian library

174. By mac9416

Even more config.py cleanup

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app.fil'
2--- app.fil 2010-01-27 00:19:45 +0000
3+++ app.fil 2017-06-11 16:11:12 +0000
4@@ -13,7 +13,7 @@
5 ./lib/wxkeryx/main.py
6 ./lib/wxkeryx/misc.py
7 ./lib/wxkeryx/options.py
8-./lib/wxkeryx/startDialog.py
9+./lib/wxkeryx/StartDialog.py
10 ./plugins/ColorMap.py
11 ./plugins/Debian.py
12 ./plugins/Search.py
13
14=== added file 'build.bat'
15--- build.bat 1970-01-01 00:00:00 +0000
16+++ build.bat 2017-06-11 16:11:12 +0000
17@@ -0,0 +1,13 @@
18+rem Get rid of any existing build.
19+rd /s /q dist
20+
21+rem Build the executable.
22+pyinstaller keryx.spec --log-level WARN
23+
24+rem Move important assets to the dist directory.
25+xcopy /e /i doc dist\doc
26+xcopy /e /i locale dist\locale
27+xcopy /e /i pixmaps dist\pixmaps
28+
29+rem Get rid of pyinstaller's temp directories. The interesting stuff is in dist.
30+rd /s /q build __pycache__
31\ No newline at end of file
32
33=== added file 'build.sh'
34--- build.sh 1970-01-01 00:00:00 +0000
35+++ build.sh 2017-06-11 16:11:12 +0000
36@@ -0,0 +1,15 @@
37+#!/usr/bin/env bash
38+
39+# Get rid of any existing build.
40+rm -r dist
41+
42+# Build the executable.
43+pyinstaller keryx.spec --log-level WARN
44+
45+# Move important assets to the dist directory.
46+cp -r doc/ dist/
47+cp -r locale/ dist/
48+cp -r pixmaps/ dist/
49+
50+# Get rid of pyinstaller's temp directories. The interesting stuff is in dist.
51+rm -r build __pycache__
52\ No newline at end of file
53
54=== removed file 'doc/INSTALL'
55--- doc/INSTALL 2009-08-09 19:36:06 +0000
56+++ doc/INSTALL 1970-01-01 00:00:00 +0000
57@@ -1,24 +0,0 @@
58-==========================
59- Installing:
60-==========================
61-
62-Windows: No installation required. Just run keryx.exe from the win32 directory
63-
64-Linux: No install required as long as Python and wxPython are installed.
65- Just run 'python keryx.py' or './keryx.py' if it is set as executable.
66-
67-==========================
68- Building:
69-==========================
70-
71-py2exe is required to build a proper windows executable.
72-
73-WARNING: Issues with Python 2.6 prevent succesful builds.
74-
75-Run 'python setup.py py2exe' to build a win32 binary. This has been tested using
76-the following:
77-
78-Python 2.5.4
79-wxPython 2.8.9.1 unicode
80-Py2exe 0.6.9
81-Pywin32
82\ No newline at end of file
83
84=== modified file 'doc/README'
85--- doc/README 2010-02-13 02:43:32 +0000
86+++ doc/README 2017-06-11 16:11:12 +0000
87@@ -7,8 +7,7 @@
88 Contact/Support:
89 ==========================
90
91- Website: http://keryxproject.org
92- Email: Chris Oliver <excid3@gmail.com>
93+ Email: Michael Crenshaw <mac9416@gmail.com>
94 Launchpad Project: http://launchpad.net/keryx
95
96 ==========================
97@@ -30,77 +29,12 @@
98 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
99
100 ==========================
101- Requirements
102-==========================
103-
104-Windows: None
105-
106-Linux: Python, wxPython
107-
108-Ubuntu users need the following packages in order to have wxPython successfully
109-installed:
110-
111-libwxbase2.8-0
112-libwxgtk2.8-0
113-python-wxversion
114-python-wxgtk2.8
115-
116-wxPython is only required for using Keryx's interface. Project creation can be
117-done via command line parameters. See USAGE for more information.
118-
119-==========================
120-Installation Instructions:
121-==========================
122-
123-Extract the Keryx archive anywhere (typically a usb device).
124-
125-==========================
126- Usage:
127-==========================
128-Keryx is based around the concept of 'projects'. Each project keeps track of the
129-software versions that are installed on that computer. To get started you must
130-first create a project on that computer.
131-
132-If you are unable to install wxPython but have Python installed you can still
133-create projects. In a console run:
134-
135-'python keryx.py --create <project name> <plugin name>'
136-
137-and Keryx will create a new project in the project directory. This is helpful
138-for Ubuntu server users along with users who have not installed wxPython yet.
139-The only <plugin name> currently available is debian.
140-
141-Once you have created this project, make sure you have a copy on your usb device
142-that will be used to run Keryx from on a computer with highspeed internet.
143-
144-Keryx can be run on ANY computer with Python and wxPython installed, meaning
145-Windows, Mac and Linux computers. Running Keryx on any Windows computer is
146-nothing more than running 'keryx.exe'. If you are running Keryx using Python, cd
147-to the directory and run 'python keryx.py' to start. Some environments support
148-running a python application by double clicking it as well.
149-
150-After you have Keryx running on an internet connected host, downloading packages
151-is as simple as opening your project and selecting the packages to download.
152-Keryx will automatically select the package dependencies for you making sure
153-that nothing is left behind. Any packages you download are saved to your
154-project's folder in the 'packages' directory.
155-
156-When you are finished you can return to your project computer and install the
157-packages through the usual way.
158-
159-==========================
160-Uninstallation/Upgrading:
161-==========================
162-
163-Extract the newest version over top, replacing all files.
164-
165-==========================
166 Thanks:
167 ==========================
168
169 Buran Ayuthia
170 John Gleissner
171-mac9416
172+mac9416
173 jacseen
174 Kevin Buente
175 Douglass Clem
176@@ -110,21 +44,3 @@
177 Ingelrest François - Plugin framework base
178 heylove - Logo is based off areao.42-Icons
179 Anyone else I forgot to mention
180-
181-==========================
182- Additional Notes:
183-==========================
184-
185-Command line parameters:
186-
187--h or --help Displays this message
188--v or --version Displays Keryx version number
189---create <project name> <plugin name> Creates a new project in the default
190- directory with name of <project name>
191- and type project <plugin name>
192---config <file> Uses <file> to as configuration file
193-
194-Due to py2exe's nature, information printed to the console is NOT displayed.
195-While the command line parameters will still work properly, no indication of
196-success will be shown in the console. The information will still be written in
197-the log files for further review.
198
199=== removed directory 'doc/Tutorial_Files'
200=== removed file 'doc/Tutorial_Files/Create_Project.png'
201Binary files doc/Tutorial_Files/Create_Project.png 2010-03-13 19:04:52 +0000 and doc/Tutorial_Files/Create_Project.png 1970-01-01 00:00:00 +0000 differ
202=== removed file 'doc/Tutorial_Files/Install_Packages.png'
203Binary files doc/Tutorial_Files/Install_Packages.png 2010-03-13 19:04:52 +0000 and doc/Tutorial_Files/Install_Packages.png 1970-01-01 00:00:00 +0000 differ
204=== removed file 'doc/Tutorial_Files/Installing_Packages.png'
205Binary files doc/Tutorial_Files/Installing_Packages.png 2010-03-13 19:04:52 +0000 and doc/Tutorial_Files/Installing_Packages.png 1970-01-01 00:00:00 +0000 differ
206=== removed file 'doc/Tutorial_Files/Open_Project.png'
207Binary files doc/Tutorial_Files/Open_Project.png 2010-03-13 19:04:52 +0000 and doc/Tutorial_Files/Open_Project.png 1970-01-01 00:00:00 +0000 differ
208=== added file 'doc/config.rst'
209--- doc/config.rst 1970-01-01 00:00:00 +0000
210+++ doc/config.rst 2017-06-11 16:11:12 +0000
211@@ -0,0 +1,8 @@
212+=======================================
213+lib.config - Configure Keryx's behavior
214+=======================================
215+
216+.. contents::
217+
218+.. automodule:: lib.config
219+ :members:
220\ No newline at end of file
221
222=== added file 'doc/debian.rst'
223--- doc/debian.rst 1970-01-01 00:00:00 +0000
224+++ doc/debian.rst 2017-06-11 16:11:12 +0000
225@@ -0,0 +1,5 @@
226+lib.debian - APT support
227+========================
228+
229+.. automodule:: lib.debian
230+ :members:
231\ No newline at end of file
232
233=== added file 'doc/index.rst'
234--- doc/index.rst 1970-01-01 00:00:00 +0000
235+++ doc/index.rst 2017-06-11 16:11:12 +0000
236@@ -0,0 +1,32 @@
237+.. Keryx documentation master file, created by
238+ sphinx-quickstart on Sun May 7 20:29:57 2017.
239+ You can adapt this file completely to your liking, but it should at least
240+ contain the root `toctree` directive.
241+
242+===================
243+Keryx documentation
244+===================
245+
246+Contents:
247+
248+.. toctree::
249+ :maxdepth: 1
250+
251+ tutorial
252+ lib
253+ config
254+ debian
255+ package
256+ project
257+ source
258+ version
259+ plugins
260+ log
261+
262+==================
263+Indices and tables
264+==================
265+
266+* :ref:`genindex`
267+* :ref:`modindex`
268+* :ref:`search`
269
270=== removed file 'doc/keryx.conf'
271--- doc/keryx.conf 2010-02-16 02:44:28 +0000
272+++ doc/keryx.conf 1970-01-01 00:00:00 +0000
273@@ -1,29 +0,0 @@
274-#### Default values ####
275-LogDir=../logs
276-LocaleDir=../locale
277-PluginsDir=../plugins
278-ProjectsDir=../projects
279-PixmapsDir=../pixmaps
280-ThemesDir=../pixmaps/themes
281-DefaultTheme=../pixmaps/themes/default
282-CurrentTheme=../pixmaps/themes/default
283-
284-#### Examples ####
285-
286-#LogDir=../logs
287-#LocaleDir=../locale
288-#PackagesDir=../packages
289-#PixmapsDir=../pixmaps
290-#PluginsDir=../plugins
291-#ProjectsDir=../projects
292-#ThemesDir=../pixmaps/themes
293-#DefaultTheme=../pixmaps/themes/default
294-#CurrentTheme=../pixmaps/themes/default
295-
296-### Proxy configuration ###
297-# Leave ProxyUsername and ProxyPassword commented
298-# when the proxy does not require them.
299-
300-#HTTPProxy=http://127.0.0.1:8880
301-#ProxyUsername=username
302-#ProxyPassword=password
303
304=== added file 'doc/lib.rst'
305--- doc/lib.rst 1970-01-01 00:00:00 +0000
306+++ doc/lib.rst 2017-06-11 16:11:12 +0000
307@@ -0,0 +1,8 @@
308+==============================
309+lib - Tools to make Keryx work
310+==============================
311+
312+.. contents::
313+
314+.. automodule:: lib
315+ :members:
316\ No newline at end of file
317
318=== added file 'doc/log.rst'
319--- doc/log.rst 1970-01-01 00:00:00 +0000
320+++ doc/log.rst 2017-06-11 16:11:12 +0000
321@@ -0,0 +1,8 @@
322+=============================
323+lib.log - Log Keryx's actions
324+=============================
325+
326+.. contents::
327+
328+.. automodule:: lib.log
329+ :members:
330\ No newline at end of file
331
332=== added file 'doc/package.rst'
333--- doc/package.rst 1970-01-01 00:00:00 +0000
334+++ doc/package.rst 2017-06-11 16:11:12 +0000
335@@ -0,0 +1,8 @@
336+======================================
337+lib.Package - Represent an APT package
338+======================================
339+
340+.. contents::
341+
342+.. automodule:: lib.package
343+ :members:
344\ No newline at end of file
345
346=== added file 'doc/plugins.rst'
347--- doc/plugins.rst 1970-01-01 00:00:00 +0000
348+++ doc/plugins.rst 2017-06-11 16:11:12 +0000
349@@ -0,0 +1,12 @@
350+=========================================
351+lib.plugins - Add UI elements dynamically
352+=========================================
353+
354+.. contents::
355+
356+.. deprecated:: 0.93
357+ This module was deprecated in 0.93 to reduce complexity. UI elements
358+ currently inserted dynamically will soon be hard-coded into the interface.
359+
360+.. automodule:: lib.plugins
361+ :members:
362\ No newline at end of file
363
364=== added file 'doc/project.rst'
365--- doc/project.rst 1970-01-01 00:00:00 +0000
366+++ doc/project.rst 2017-06-11 16:11:12 +0000
367@@ -0,0 +1,8 @@
368+====================================
369+lib.project - Manage a Keryx project
370+====================================
371+
372+.. contents::
373+
374+.. automodule:: lib.project
375+ :members:
376\ No newline at end of file
377
378=== added file 'doc/source.rst'
379--- doc/source.rst 1970-01-01 00:00:00 +0000
380+++ doc/source.rst 2017-06-11 16:11:12 +0000
381@@ -0,0 +1,8 @@
382+====================================
383+lib.source - Manage software sources
384+====================================
385+
386+.. contents::
387+
388+.. automodule:: lib.source
389+ :members:
390\ No newline at end of file
391
392=== added file 'doc/tutorial.rst'
393--- doc/tutorial.rst 1970-01-01 00:00:00 +0000
394+++ doc/tutorial.rst 2017-06-11 16:11:12 +0000
395@@ -0,0 +1,68 @@
396+##############
397+Keryx Tutorial
398+##############
399+
400+.. contents::
401+
402+*************
403+Getting Keryx
404+*************
405+
406+Grab Keryx from the Download page and extract it to your flash drive.
407+
408+**************
409+Starting Keryx
410+**************
411+
412+On Linux
413+========
414+
415+Navigate to the keryx/linux directory on your flash drive and double-click the 'keryx' icon.
416+
417+On Windows
418+==========
419+
420+Navigate to the keryx/win32 directory on your flash drive and double-click the 'keryx' or 'keryx.exe' icon.
421+
422+*************************************
423+Creating a Project (Offline Computer)
424+*************************************
425+
426+After starting Keryx, enter the name of your project (or leave the default) and click 'New Project'.
427+
428+When asked whether you would like to download the latest package lists, choose 'No', then exit Keryx.
429+
430+***********************************
431+Opening a Project (Online Computer)
432+***********************************
433+
434+Start Keryx and choose your project from the bottom drop-down menu. Then click 'Open Project'.
435+
436+If you would like to have the latest packages available for download, choose 'Yes' when prompted whether to download latest package lists (recommended).
437+
438+**************************************
439+Installing Packages (Offline Computer)
440+**************************************
441+
442+Open your project and select 'Install Packages' from the 'Project' menu. Check all the packages you want to install and click 'Continue'.
443+
444+A terminal will display the progress of package installation. After installation is complete, press ENTER.
445+
446+So that Keryx will know that you have installed new packages, you should now update the project status (Project > Update Status).
447+
448+*****************************
449+What if something goes wrong?
450+*****************************
451+
452+There are several ways to get help with a problem. You can
453+
454+ * report a bug on Launchpad
455+ * ask on the Forums or on Launchpad
456+ * contact our mailing list
457+ * chat with us in #keryx on Freenode
458+
459+*****************************
460+And if everything goes right?
461+*****************************
462+
463+Please consider donating to the project!
464\ No newline at end of file
465
466=== added file 'doc/version.rst'
467--- doc/version.rst 1970-01-01 00:00:00 +0000
468+++ doc/version.rst 2017-06-11 16:11:12 +0000
469@@ -0,0 +1,8 @@
470+==================================================
471+lib.version - Tools to handle APT package versions
472+==================================================
473+
474+.. contents::
475+
476+.. automodule:: lib.version
477+ :members:
478\ No newline at end of file
479
480=== modified file 'keryx.py'
481--- keryx.py 2010-03-12 03:02:38 +0000
482+++ keryx.py 2017-06-11 16:11:12 +0000
483@@ -19,122 +19,41 @@
484
485 # Import commands here, even though we don't use it because debian.py uses it
486 # and our plugin system confuses pyinstaller. We need to manually specify this
487-# in keryx so that pyinstaller can grab it and bundle the code
488-
489-# Import modules to satisfy plugin dependencies:
490-import commands
491-
492-# Then import everything else.
493-import os
494+# in Keryx so that pyinstaller can grab it and bundle the code
495+
496 import sys
497
498-from lib import consts
499-
500-#####################
501-# Set configuration #
502-#####################
503-if ('--config' in sys.argv): # If config is specified
504- index = sys.argv.index('--config')
505- try: filename = sys.argv[index + 1]
506- except:
507- print 'ERROR: No config filename given'
508- sys.exit(1)
509-
510-
511- # Reinitialize consts if the a specific file has been passed
512- import lib.config
513- lib.config.fromFile(filename)
514-
515-# Else if file_config exists, load it
516-elif os.path.exists(consts.file_config):
517- import lib.config
518- lib.config.fromFile(consts.file_config)
519-
520-# Import log and project AFTER configuration has been set
521-from lib import log, project
522-
523-################
524-# Translations #
525-################
526+from lib import config, log
527+
528+# Handle translations
529+import gettext
530+import locale
531+
532+_ = gettext.gettext
533+gettext.install(config.name.lower(), config.locale_dir)
534+
535+lang, encoding = locale.getdefaultlocale()
536+
537 try:
538- import gettext
539- import locale
540- gettext.install(consts.appNameShort, consts.dirLocale, unicode=True)
541-
542- lang = locale.getdefaultlocale()[0]
543- lang = gettext.translation(consts.appNameShort, consts.dirLocale, languages=[lang])
544+ lang = gettext.translation(config.name.lower(),
545+ config.locale_dir,
546+ languages=[lang])
547 lang.install()
548-except: pass
549+except FileNotFoundError:
550+ print(_('Could not locate a translation file for your language.'))
551
552-#TODO: Set wxWidgets translation information
553+# TODO: Set wxWidgets translation information
554
555-###################
556-# Parse arguments #
557-###################
558-if ('--help' in sys.argv) or ('-h' in sys.argv):
559- log.info(consts.parameters)
560- sys.exit(0)
561-
562+# Parse arguments
563 if ('--version' in sys.argv) or ('-v' in sys.argv):
564- log.info('%s %s' % (consts.appName, consts.appVersion))
565+ log.info('%s %s' % (config.name, config.version))
566 sys.exit(0)
567
568-if ('--create' in sys.argv):
569- import platform
570- from lib import plugins, project
571- try:
572- index = sys.argv.index('--create')
573- name = sys.argv[index + 1]
574- plugin_name = 'debian'
575- except:
576- log.error(_('Unable to create project'))
577- sys.exit(1)
578- plugins.load(consts.dirPlugins, '', False) # Don't load interface plugins
579- for item in range(0,len(plugins.OSPluginList)):
580- if plugin_name == plugins.OSPluginList[item][0].lower():
581- # Append new project
582- project.projects.append(project.Project())
583- proj = project.projects[len(project.projects) - 1]
584-
585- # Create project
586- success, filename = proj.CreateKeryx(name,
587- plugins.OSPluginList[item][0],
588- plugins.OSPluginList[item][1])
589-
590- if success:
591- log.info(_('Project created successfully.'))
592- sys.exit(0)
593- log.error(_('Unable to create project.'))
594- log.info(_('Make sure a project by this name does not already exist and you have selected the right plugin for this project.'))
595- sys.exit(1)
596-
597-
598-WXVER = "2.8"
599-
600-if not hasattr(sys, "frozen"): # If this isn't a compiled version of Keryx...
601- import wxversion
602- if wxversion.checkInstalled(WXVER):
603- wxversion.select(WXVER)
604- else:
605- import wx
606- app = wx.PySimpleApp()
607- wx.MessageBox("Warning: The requested version of wxPython is not installed.\n"
608- "Please install version %s" % WXVER,
609- "wxPython Version Warning")
610- app.MainLoop()
611-
612-#############################
613-# Attempt to load interface #
614-#############################
615-#try:
616-if 1:
617- import lib.wxkeryx
618- lib.wxkeryx.Start()
619-#except Exception, e:
620- #print e
621-# log.error(_('Unable to load interface.'))
622-# log.info(_('Make sure you have wxPython installed.'))
623-# log.info(_('Read the README for details on installing wxPython'))
624-# log.info(_('You can you \'python keryx.py --create\' to create a new project through the command-line if you do not have wxPython installed'))
625-# log.info(_('Use \'python keryx.py --help\' to see all command-line parameters.'))
626-# sys.exit(1)
627+if not hasattr(sys, 'frozen'): # If this isn't a compiled version of Keryx...
628+ import wx
629+ app = wx.App()
630+ app.MainLoop()
631+
632+# Attempt to load interface
633+from lib import wxkeryx
634+wxkeryx.start()
635
636=== added file 'keryx.spec'
637--- keryx.spec 1970-01-01 00:00:00 +0000
638+++ keryx.spec 2017-06-11 16:11:12 +0000
639@@ -0,0 +1,29 @@
640+# -*- mode: python -*-
641+
642+block_cipher = None
643+
644+
645+a = Analysis(['keryx.py'],
646+ pathex=['/home/tony/PycharmProjects/keryx'],
647+ binaries=[],
648+ datas=[],
649+ hiddenimports=[],
650+ hookspath=[],
651+ runtime_hooks=[],
652+ excludes=[],
653+ win_no_prefer_redirects=False,
654+ win_private_assemblies=False,
655+ cipher=block_cipher)
656+pyz = PYZ(a.pure, a.zipped_data,
657+ cipher=block_cipher)
658+exe = EXE(pyz,
659+ a.scripts,
660+ a.binaries,
661+ a.zipfiles,
662+ a.datas,
663+ name='keryx',
664+ debug=False,
665+ strip=False,
666+ upx=True,
667+ console=False,
668+ icon='pixmaps/keryx.ico')
669
670=== modified file 'lib/__init__.py'
671--- lib/__init__.py 2010-03-05 00:06:56 +0000
672+++ lib/__init__.py 2017-06-11 16:11:12 +0000
673@@ -17,30 +17,54 @@
674 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
675
676 import os
677-import webbrowser
678-
679-import consts
680+import subprocess
681+
682+from . import config
683+
684+
685+class FreeSpaceUnavailable(Exception):
686+ """This means there's no way to get the free space on some device."""
687+ pass
688
689 try:
690- import win32file
691+ from win32file import GetDiskFreeSpaceEx
692 except ImportError:
693- pass # Probably not a Windows system.
694-
695-def driveFreeSpace():
696- """Retrieves the cwd's filesystem stats"""
697- if os.name == 'posix': # get free space if they have statvfs
698- data = os.statvfs(consts.cwd)
699+ def GetDiskFreeSpaceEx(drive):
700+ """This is a placeholder for if win32file is unavailable.
701+
702+ :param str drive: the name of the drive to check for free space
703+ """
704+ raise FreeSpaceUnavailable
705+
706+
707+def drive_free_space():
708+ """Retrieve the current working directory's free space.
709+
710+ :return: the remaining space in KiB, MiB, or GiB
711+ :rtype: str
712+ """
713+ if os.name == 'posix': # get free space if they have statvfs
714+ data = os.statvfs(config.cwd)
715 return convert_file_size(float(data[0] * data[4]))
716 if os.name == 'nt':
717- drive = os.path.splitdrive(consts.cwd)[0] # Gets drive letter
718- d_size = win32file.GetDiskFreeSpaceEx(drive)
719+ drive = os.path.splitdrive(config.cwd)[0] # Gets drive letter
720+ try:
721+ d_size = GetDiskFreeSpaceEx(drive)
722+ except FreeSpaceUnavailable:
723+ return 'Unavailable'
724 return convert_file_size(d_size[2])
725 # unsupported OS, return error
726 return 'Unavailable'
727+
728+
729+def convert_file_size(num_bytes):
730+ """Convert file size from bytes to appropriate size.
731
732-def convert_file_size(bytes):
733- """Converts file size from bytes to appropriate size"""
734- kb = bytes / 1024.0
735+ :param int num_bytes: the number of bytes to be formatted
736+ :return: a nice representation of the size, in KiB, MiB, or GiB
737+ :rtype: str
738+ """
739+ kb = num_bytes / 1024.0
740 if kb < 1000:
741 return '%.1f %s' % (kb, 'KiB')
742 mb = kb / 1024.0
743@@ -49,12 +73,9 @@
744 gb = mb / 1024.0
745 return '%.2f %s' % (gb, 'GiB')
746
747-def browserOpen(url):
748- """Opens default browser to url"""
749- webbrowser.open(url)
750
751 def utf(desc):
752- """Returns string in fully UTF compatible format"""
753+ """Convert the string to a fully UTF compatible format."""
754 new_word = ''
755 for letter in desc:
756 if ord(letter) > 128:
757@@ -62,14 +83,54 @@
758 new_word += letter
759 return new_word
760
761-def joinUrl(first, last):
762- """ Returns full URL """
763- if first.endswith('/'):
764- if last.startswith('/'): return first + last[1:]
765- else: return first + last
766+
767+def join_url(first, *args):
768+ """Combines the given elements into a full URL.
769+
770+ :param str first: the first part of the URL
771+ :param str args: subsequent parts of the URL
772+ :return: a complete URL from the given parts
773+ :rtype: str
774+ """
775+ if len(args) == 0:
776+ return first
777+ elif len(args) == 1:
778+ return first.strip('/') + '/' + args[0].strip('/')
779 else:
780- if last.startswith('/'): return first + last
781- else: return first + '/' + last
782-
783-
784-
785+ return join_url(first.strip('/') + '/' + args[0].strip('/'), *args[1:])
786+
787+
788+def run_root(program, args=''):
789+ """Run the given program with the given arguments, gaining root privileges
790+ using some GUI-based su/sudo program. The first available from among the
791+ choices gksu, gksudo, kdesu, and kdesudo will be used.
792+
793+ :param str program: the command to be run
794+ :param str args: the arguments to run with the command
795+ :return: a success/error code and a message
796+ :rtype: list(int, str)
797+ """
798+ # Find an available privilege-escalating tool.
799+ root = ''
800+ for a in ('gksu', 'gksudo', 'kdesu', 'kdesudo'):
801+ if subprocess.run('which ' + a).returncode == 0:
802+ root = a
803+ break
804+
805+ # Give compatible GUI su's a shorter command description
806+ if root.startswith('gk'):
807+ description = '%s %s' % (program, args)
808+ if len(description) > 50:
809+ description = '--description \'%s...\'' % (description[:46])
810+ else: # Certain su GUIs do not accept alternate descriptions
811+ description = ''
812+
813+ # If no tool was available, return an error code.
814+ # TODO: actually raise an error.
815+ if root == '':
816+ return 64, 'Could not find superuser access'
817+
818+ # Attempt to run the program as root.
819+ proc = subprocess.run('%s %s -- %s %s' %
820+ (root, description, program, args))
821+ return proc.returncode, ''
822
823=== added file 'lib/config.py'
824--- lib/config.py 1970-01-01 00:00:00 +0000
825+++ lib/config.py 2017-06-11 16:11:12 +0000
826@@ -0,0 +1,96 @@
827+import os
828+import sys
829+from os.path import abspath, join
830+from gettext import gettext as _
831+
832+cwd = abspath(os.path.dirname(sys.argv[0]))
833+
834+
835+class Theme:
836+ """Represents a GUI theme by defining UI elements (mostly icons)."""
837+ def __init__(self, theme_dir):
838+ """Set theme asset paths according to the given theme directory.
839+
840+ :param str theme_dir: the directory containing a UI theme
841+ """
842+ self.path = theme_dir
843+ self.icon_about = join(self.path, 'about.png')
844+ self.icon_arrow_down = join(self.path, 'arrow_down.png')
845+ self.icon_arrow_up = join(self.path, 'arrow_up.png')
846+ self.icon_book_open = join(self.path, 'book_open.png')
847+ self.icon_bug_report = join(self.path, 'bug.png')
848+ self.icon_close = join(self.path, 'close.png')
849+ self.icon_donate = join(self.path, 'donate.png')
850+ self.icon_download = join(self.path, 'download.png')
851+ self.icon_downloaded = join(self.path, 'downloaded.png')
852+ self.icon_error = join(self.path, 'error.png')
853+ self.icon_find = join(self.path, 'find.png')
854+ self.icon_help = join(self.path, 'help.png')
855+ self.icon_home = join(self.path, 'home.png')
856+ self.icon_install = join(self.path, 'install.png')
857+ self.icon_layout = join(self.path, 'layout.png')
858+ self.icon_new = join(self.path, 'new.png')
859+ self.icon_open = join(self.path, 'open.png')
860+ self.icon_options = join(self.path, 'options.png')
861+ self.icon_package = join(self.path, 'package.png')
862+ self.icon_plugin = join(self.path, 'plugin.png')
863+ self.icon_plugin_add = join(self.path, 'plugin_add.png')
864+ self.icon_plugin_disable = join(self.path, 'plugin_disabled.png')
865+ self.icon_project_details = join(self.path, 'project_details.png')
866+ self.icon_quit = join(self.path, 'quit.png')
867+ self.icon_refresh = join(self.path, 'refresh.png')
868+ self.icon_sources = join(self.path, 'sources.png')
869+ self.icon_translate = join(self.path, 'translate.png')
870+ self.icon_update = join(self.path, 'update.png')
871+ self.icon_updates = join(self.path, 'get_updates.png')
872+ self.icon_uptodate = join(self.path, 'uptodate.png')
873+
874+# Strings
875+name = 'Keryx'
876+subtitle = _('Bringing Updates Home.')
877+version = '0.92.5'
878+
879+authors = 'Chris Oliver\nBuran Ayuthia\nmac9416\njacseen'
880+email = 'excid3@gmail.com'
881+artists = 'Chris Oliver'
882+docwriters = 'Chris Oliver'
883+translators = ''
884+copyright = '(C) 2008-2009 Chris Oliver'
885+description = name + _(' is a package manager for computers without internet.')
886+license = name + ' is free software; you can redistribute ' \
887+ 'it and/or modify it under the terms of the GNU General Public ' \
888+ 'License as published by the Free Software Foundation; either ' \
889+ 'version 2 of the License, or (at your option) any later version.\n' \
890+ 'Keryx is distributed in the hope that it will be useful, but ' \
891+ 'WITHOUT ANY WARRANTY; without even the implied warranty of ' \
892+ 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ' \
893+ 'General Public License for more details. You should have received ' \
894+ 'a copy of the GNU General Public License along with Keryx; if ' \
895+ 'not, write to the Free Software Foundation, Inc., 59 Temple ' \
896+ 'Place, Suite 330, Boston, MA 02111-1307 USA'
897+
898+# URLs
899+bug_url = 'https://bugs.launchpad.net/keryx'
900+donate_url = 'http://keryxproject.org/donate'
901+help_url = 'http://keryxproject.org/forum'
902+homepage_url = 'http://keryxproject.org'
903+tutorial_url = join(cwd, 'doc', 'sphinx', 'tutorial.html')
904+translate_url = 'https://translations.launchpad.net/keryx'
905+
906+# Directories
907+locale_dir = abspath(join(cwd, 'locale'))
908+log_dir = abspath(join(cwd, 'logs'))
909+projects_dir = abspath(join(cwd, 'projects'))
910+
911+# Files
912+log_path = join(log_dir, 'log')
913+
914+# UI assets paths
915+pixmaps_dir = join(cwd, 'pixmaps')
916+themes_dir = join(pixmaps_dir, 'themes')
917+default_theme_dir = join(themes_dir, 'default')
918+theme = Theme(default_theme_dir)
919+
920+# Logo
921+ico_path = join(pixmaps_dir, 'keryx.ico')
922+logo_path = join(pixmaps_dir, 'keryx.png')
923
924=== removed file 'lib/config.py'
925--- lib/config.py 2010-03-13 19:04:52 +0000
926+++ lib/config.py 1970-01-01 00:00:00 +0000
927@@ -1,113 +0,0 @@
928-# -*- coding: utf-8 -*-
929-#
930-# Author: Chris Oliver (excid3@gmail.com)
931-#
932-# This program is free software; you can redistribute it and/or modify
933-# it under the terms of the GNU General Public License as published by
934-# the Free Software Foundation; either version 2 of the License, or
935-# (at your option) any later version.
936-#
937-# This program is distributed in the hope that it will be useful,
938-# but WITHOUT ANY WARRANTY; without even the implied warranty of
939-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
940-# GNU Library General Public License for more details.
941-#
942-# You should have received a copy of the GNU General Public License
943-# along with this program; if not, write to the Free Software
944-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
945-
946-import consts
947-import os.path
948-#from gettext import gettext as _
949-
950-def genDir(val):
951- return os.path.abspath(os.path.join(consts.cwd, val))
952-
953-def fromFile(filename):
954- # Import settings
955- try:
956- print 'Loading config: ' + filename
957- consts.file_config = filename
958- data = open(filename, 'r').read()
959- data = data.split('\n')
960- for item in data:
961- try:
962- key, val = item.split('=')
963- if key == 'LogDir':
964- consts.LogPath = val
965- consts.dirLog = genDir(val)
966- consts.fileLog = os.path.join(consts.dirLog, 'log')
967- if key == 'LocaleDir':
968- consts.LocalePath = val
969- consts.dirLocale = genDir(val)
970-# if key == 'PackagesDir':
971-# consts.PackagesPath = val
972-# consts.dirPackages = genDir(val)
973- if key == 'PixmapsDir':
974- consts.PixmapsPath = val
975- consts.dirPixmaps = genDir(val)
976- pixmapsChange()
977- if key == 'PluginsDir':
978- consts.PluginsPath = val
979- consts.dirPlugins = genDir(val)
980- if key == 'ProjectsDir':
981- consts.ProjectsPath = val
982- consts.dirProjects = genDir(val)
983- if key == 'ThemesDir':
984- consts.ThemesPath = val
985- consts.dirThemes = genDir(val)
986- if key == 'DefaultTheme':
987- consts.ThemeDefaultPath = val
988- consts.dirThemeDefault = genDir(val)
989- if key == 'CurrentTheme':
990- consts.CurrentThemePath = val
991- consts.dirCurrentTheme = genDir(val)
992- themeChange()
993- if key == 'HTTPProxy':
994- consts.proxy_enabled = True
995- consts.http_proxy = {'http':val}
996- if key == 'ProxyUsername': consts.proxy_username = val
997- if key == 'ProxyPassword': consts.proxy_password = val
998- except: pass
999- consts.file_config = filename # Set the file_config location, so that if the config is change, settings go back to there
1000- except Exception, e:
1001- print e
1002-
1003-def pixmapsChange():
1004- # Logo
1005- consts.fileIco = os.path.join(consts.dirPixmaps,'keryx.ico')
1006- consts.fileLogo = os.path.join(consts.dirPixmaps,'keryx.png')
1007-
1008-def themeChange():
1009- """Load theme images"""
1010- consts.icon_about = os.path.join(consts.dirCurrentTheme, 'about.png')
1011- consts.icon_arrow_down = os.path.join(consts.dirCurrentTheme, 'arrow_down.png')
1012- consts.icon_arrow_up = os.path.join(consts.dirCurrentTheme, 'arrow_up.png')
1013- consts.icon_bug_report = os.path.join(consts.dirCurrentTheme, 'bug.png')
1014- consts.icon_book_open = os.path.join(consts.dirCurrentTheme, 'book_open.png')
1015- consts.icon_close = os.path.join(consts.dirCurrentTheme, 'close.png')
1016- consts.icon_donate = os.path.join(consts.dirCurrentTheme, 'donate.png')
1017- consts.icon_download = os.path.join(consts.dirCurrentTheme, 'download.png')
1018- #icon_download_package = os.path.join(dirCurrentTheme, 'download_package.png')
1019- consts.icon_downloaded = os.path.join(consts.dirCurrentTheme, 'downloaded.png')
1020- consts.icon_error = os.path.join(consts.dirCurrentTheme, 'error.png')
1021- consts.icon_find = os.path.join(consts.dirCurrentTheme, 'find.png')
1022- consts.icon_help = os.path.join(consts.dirCurrentTheme, 'help.png')
1023- consts.icon_home = os.path.join(consts.dirCurrentTheme, 'home.png')
1024- consts.icon_install = os.path.join(consts.dirCurrentTheme, 'install.png')
1025- consts.icon_layout = os.path.join(consts.dirCurrentTheme, 'layout.png')
1026- consts.icon_new = os.path.join(consts.dirCurrentTheme, 'new.png')
1027- consts.icon_open = os.path.join(consts.dirCurrentTheme, 'open.png')
1028- consts.icon_options = os.path.join(consts.dirCurrentTheme, 'options.png')
1029- consts.icon_package = os.path.join(consts.dirCurrentTheme, 'package.png')
1030- consts.icon_plugin = os.path.join(consts.dirCurrentTheme, 'plugin.png')
1031- consts.icon_plugin_add = os.path.join(consts.dirCurrentTheme, 'plugin_add.png')
1032- consts.icon_plugin_disable = os.path.join(consts.dirCurrentTheme, 'plugin_disabled.png')
1033- consts.icon_project_details = os.path.join(consts.dirCurrentTheme, 'project_details.png')
1034- consts.icon_quit = os.path.join(consts.dirCurrentTheme, 'quit.png')
1035- consts.icon_refresh = os.path.join(consts.dirCurrentTheme, 'refresh.png')
1036- consts.icon_sources = os.path.join(consts.dirCurrentTheme, 'sources.png')
1037- consts.icon_translate = os.path.join(consts.dirCurrentTheme, 'translate.png')
1038- consts.icon_update = os.path.join(consts.dirCurrentTheme, 'update.png')
1039- consts.icon_updates = os.path.join(consts.dirCurrentTheme, 'get_updates.png')
1040- consts.icon_uptodate = os.path.join(consts.dirCurrentTheme, 'uptodate.png')
1041
1042=== removed file 'lib/consts.py'
1043--- lib/consts.py 2010-03-13 19:04:52 +0000
1044+++ lib/consts.py 1970-01-01 00:00:00 +0000
1045@@ -1,150 +0,0 @@
1046-# -*- coding: utf-8 -*-
1047-#
1048-# Author: Chris Oliver (excid3@gmail.com)
1049-#
1050-# This program is free software; you can redistribute it and/or modify
1051-# it under the terms of the GNU General Public License as published by
1052-# the Free Software Foundation; either version 2 of the License, or
1053-# (at your option) any later version.
1054-#
1055-# This program is distributed in the hope that it will be useful,
1056-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1057-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1058-# GNU Library General Public License for more details.
1059-#
1060-# You should have received a copy of the GNU General Public License
1061-# along with this program; if not, write to the Free Software
1062-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1063-
1064-import os
1065-import sys
1066-from gettext import gettext as _
1067-
1068-cwd = os.path.abspath(os.path.dirname(sys.argv[0]))
1069-
1070-# Strings
1071-appName = 'Keryx'
1072-appVersion = '0.92.4'
1073-appNameShort = 'keryx'
1074-appFileExt = '.keryx'
1075-appComments = _('Bringing Updates Home.')
1076-filterDesc = _('Keryx Project Files') + ' | (*' + appFileExt + ')'
1077-
1078-authors = 'Chris Oliver\nBuran Ayuthia\nmac9416\njacseen'
1079-email = 'excid3@gmail.com'
1080-artists = 'Chris Oliver'
1081-docwriters = 'Chris Oliver'
1082-translators = ''
1083-copyright = '(C) 2008-2009 Chris Oliver'
1084-description = appName + _(' is a package manager for computers without internet.')
1085-license = appName + _(""" is free software; you can redistribute it and/or
1086-modify it under the terms of the GNU General Public License as published by the
1087-Free Software Foundation; either version 2 of the License, or (at your option)
1088-any later version.
1089-
1090-Keryx is distributed in the hope that it will be useful, but WITHOUT ANY
1091-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1092-PARTICULAR PURPOSE. See the GNU General Public License for more details. You
1093-should have received a copy of the GNU General Public License along with Keryx;
1094-if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite
1095-330, Boston, MA 02111-1307 USA""")
1096-
1097-welcome_message = _('Welcome to ') + appName
1098-
1099-parameters="""
1100-Command line parameters:
1101--h or --help Displays this message
1102--v or --version Displays Keryx version number
1103---create <project name> <plugin name> Creates a new project in the default
1104- directory with name of <project name>
1105- and type project <plugin name>
1106---config <file> Uses <file> to as configuration file\n"""
1107-
1108-# Proxy
1109-proxy_enabled = False
1110-http_proxy = {}
1111-proxy_username = ''
1112-proxy_password = ''
1113-
1114-# URLs
1115-urlBug = 'https://bugs.launchpad.net/keryx'
1116-urlDonate = 'http://keryxproject.org/donate'
1117-urlHelp = 'http://keryxproject.org/forum'
1118-urlHomepage = 'http://keryxproject.org'
1119-urlTutorial = os.path.join(os.path.join(cwd, 'doc'), 'Tutorial.html')
1120-urlTranslate = 'https://translations.launchpad.net/keryx'
1121-
1122-columns = [(_("S"), 50), (_("Package Name"), 200), (_("Installed Version"), 200), (_("Latest Version"), 200), (_("Description"), 200)]
1123-
1124-# Directories
1125-LocalePath = 'locale'
1126-LogPath = 'logs'
1127-#PackagesPath = 'packages'
1128-PixmapsPath = 'pixmaps'
1129-PluginsPath = 'plugins'
1130-ProjectsPath = 'projects'
1131-ThemesPath = PixmapsPath + '/themes'
1132-ThemeDefaultPath = ThemesPath + '/default'
1133-CurrentThemePath = ThemeDefaultPath
1134-
1135-dirLocale = os.path.abspath(os.path.join(cwd, LocalePath))
1136-dirLog = os.path.abspath(os.path.join(cwd, LogPath))
1137-#dirPackages = os.path.abspath(os.path.join(cwd, PackagesPath))
1138-dirPixmaps = os.path.abspath(os.path.join(cwd, PixmapsPath))
1139-dirPlugins = os.path.abspath(os.path.join(cwd, PluginsPath))
1140-dirProjects = os.path.abspath(os.path.join(cwd, ProjectsPath))
1141-dirThemes = os.path.abspath(os.path.join(cwd, ThemesPath))
1142-dirThemeDefault = os.path.abspath(os.path.join(cwd, ThemeDefaultPath))
1143-dirCurrentTheme = os.path.abspath(os.path.join(cwd, CurrentThemePath))
1144-
1145-# Files
1146-configFilename = appNameShort + '.conf'
1147-file_config = os.path.join(cwd, configFilename)
1148-fileLog = os.path.join(dirLog, 'log')
1149-
1150-# Filetypes
1151-wildcard = '%s (*.keryx)|*.keryx|' \
1152- '%s (*.*)|*.*' % (_("Keryx project"), _("All files"))
1153-wildcard_plugin = '%s (*.py)|*.py|' \
1154- '%s (*.*)|*.*' % (_("Python File"), _("All files"))
1155-
1156-# Logo
1157-fileIco = os.path.join(dirPixmaps,'keryx.ico')
1158-fileLogo = os.path.join(dirPixmaps,'keryx.png')
1159-
1160-# Load theme images
1161-icon_about = os.path.join(dirCurrentTheme, 'about.png')
1162-icon_arrow_down = os.path.join(dirCurrentTheme, 'arrow_down.png')
1163-icon_arrow_up = os.path.join(dirCurrentTheme, 'arrow_up.png')
1164-icon_book_open = os.path.join(dirCurrentTheme, 'book_open.png')
1165-icon_bug_report = os.path.join(dirCurrentTheme, 'bug.png')
1166-icon_close = os.path.join(dirCurrentTheme, 'close.png')
1167-icon_donate = os.path.join(dirCurrentTheme, 'donate.png')
1168-icon_download = os.path.join(dirCurrentTheme, 'download.png')
1169-#icon_download_package = os.path.join(dirCurrentTheme, 'download_package.png')
1170-icon_downloaded = os.path.join(dirCurrentTheme, 'downloaded.png')
1171-icon_error = os.path.join(dirCurrentTheme, 'error.png')
1172-icon_find = os.path.join(dirCurrentTheme, 'find.png')
1173-icon_help = os.path.join(dirCurrentTheme, 'help.png')
1174-icon_home = os.path.join(dirCurrentTheme, 'home.png')
1175-icon_install = os.path.join(dirCurrentTheme, 'install.png')
1176-icon_layout = os.path.join(dirCurrentTheme, 'layout.png')
1177-icon_new = os.path.join(dirCurrentTheme, 'new.png')
1178-icon_open = os.path.join(dirCurrentTheme, 'open.png')
1179-icon_options = os.path.join(dirCurrentTheme, 'options.png')
1180-icon_package = os.path.join(dirCurrentTheme, 'package.png')
1181-icon_plugin = os.path.join(dirCurrentTheme, 'plugin.png')
1182-icon_plugin_add = os.path.join(dirCurrentTheme, 'plugin_add.png')
1183-icon_plugin_disable = os.path.join(dirCurrentTheme, 'plugin_disabled.png')
1184-icon_project_details = os.path.join(dirCurrentTheme, 'project_details.png')
1185-icon_quit = os.path.join(dirCurrentTheme, 'quit.png')
1186-icon_refresh = os.path.join(dirCurrentTheme, 'refresh.png')
1187-icon_sources = os.path.join(dirCurrentTheme, 'sources.png')
1188-icon_translate = os.path.join(dirCurrentTheme, 'translate.png')
1189-icon_update = os.path.join(dirCurrentTheme, 'update.png')
1190-icon_updates = os.path.join(dirCurrentTheme, 'get_updates.png')
1191-icon_uptodate = os.path.join(dirCurrentTheme, 'uptodate.png')
1192-
1193-#color_error = wx.Color(255,118,106)
1194-#color_update = "light blue"
1195-#color_uptodate = wx.Color(150, 235, 140)
1196
1197=== added file 'lib/debian.py'
1198--- lib/debian.py 1970-01-01 00:00:00 +0000
1199+++ lib/debian.py 2017-06-11 16:11:12 +0000
1200@@ -0,0 +1,530 @@
1201+# -*- coding: utf-8 -*-
1202+#
1203+# Author: Chris Oliver (excid3@gmail.com)
1204+#
1205+# This program is free software; you can redistribute it and/or modify
1206+# it under the terms of the GNU General Public License as published by
1207+# the Free Software Foundation; either version 2 of the License, or
1208+# (at your option) any later version.
1209+#
1210+# This program is distributed in the hope that it will be useful,
1211+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1212+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1213+# GNU Library General Public License for more details.
1214+#
1215+# You should have received a copy of the GNU General Public License
1216+# along with this program; if not, write to the Free Software
1217+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1218+
1219+import os
1220+import platform
1221+import shutil
1222+import subprocess
1223+from gettext import gettext as _
1224+from os.path import join as path_join
1225+
1226+import lib
1227+from lib import config
1228+from lib import log
1229+from lib import run_root
1230+from lib.source import Source
1231+from lib.package import Package
1232+from lib.version import version_compare
1233+
1234+
1235+FILE = 'debian.conf'
1236+
1237+# Set this plugin's attributes
1238+NAME = 'Debian'
1239+TYPE = 'OS'
1240+VERSION = '0.92.4'
1241+AUTHOR = 'Chris Oliver <excid3@gmail.com>'
1242+
1243+
1244+def is_os():
1245+ """Determines whether the current OS uses APT
1246+
1247+ :return: true if the current OS uses APT, false otherwise
1248+ :rtype: bool
1249+ """
1250+ return os.path.exists('/etc/apt/')
1251+
1252+
1253+def create_project(name):
1254+ """Creates project files
1255+
1256+ :param str name: the name of the new project being created
1257+ """
1258+ dir_new = path_join(config.projects_dir, name)
1259+
1260+ # Copy relevant files to the project directory.
1261+ __grab_files(dir_new)
1262+
1263+ # Write project info to a file in the project directory.
1264+ with open(path_join(dir_new, FILE), 'w') as outfile:
1265+ outfile.write('Computer Name: %s\n' % get_comp_name() +
1266+ 'OS Name: %s\n' % get_os_name() +
1267+ 'OS Version: %s\n' % get_os_version() +
1268+ 'Architecture: %s\n' % get_os_arch() +
1269+ 'Kernel: %s\n' % get_kernel_name())
1270+
1271+
1272+def load_local_package_list(directory, arch):
1273+ """Load package information from locally-stored lists.
1274+
1275+ :param str directory: the directory to load lists from
1276+ :param str arch: the OS architecture to load lists for
1277+ :return: a dict populated with package information
1278+ :rtype: dict
1279+ """
1280+ list_dir = path_join(directory, 'lists')
1281+ debs = __parse_sources(directory)
1282+ filenames = __files_from_debs(debs, arch)
1283+ installed = __get_installed(directory)
1284+
1285+ # Initialize vars
1286+ packages = {}
1287+
1288+ for item in filenames:
1289+ # TODO: better enforce zipping/unzipping of list files.
1290+ path = path_join(list_dir, item)
1291+ if not os.path.exists(path):
1292+ path = path.strip('.gz')
1293+ with open(path, 'r') as data:
1294+ url = 'http://%s' % item.split('_dists')[0].replace('_', '/')
1295+ # Append all packages to list
1296+ packages = __read_packages(data, installed, packages, url)
1297+
1298+ return packages
1299+
1300+
1301+def load_remote_package_list(directory, arch):
1302+ """Get information to load package lists from the project's repos.
1303+
1304+ :param str directory: the project's directory, to load source info from
1305+ :param str arch: '32bit' or '64bit'
1306+ :return: pairs of (url, destination) pairs
1307+ :rtype: list(list)
1308+ """
1309+ list_dir = path_join(directory, 'lists')
1310+ debs = __parse_sources(directory)
1311+ # TODO: extract hashes for list files from Release files
1312+ # and 'return zip(urls, temp_names, checksums)' instead
1313+ temp_names = __files_from_debs(debs, arch, list_dir)
1314+ urls = __urls_from_debs(debs, arch)
1315+
1316+ # FIXME: don't return a list, just fix everything that expects a list
1317+ # instead of an iterator.
1318+ return list(zip(urls, temp_names)) # Returns urls, gzip file locations
1319+
1320+
1321+def get_dependencies(directory, all_packages, package_name):
1322+ """Takes package name and returns package information for each dependency
1323+ (recursive).
1324+
1325+ :param str directory: the directory to place downloaded packages in
1326+ :param dict all_packages: the dict holding all the package info
1327+ :param str package_name: the name of the package to get dependencies for
1328+ :return: a list of URLs to download package dependencies
1329+ :rtype: list(str)
1330+ """
1331+ urls = []
1332+ if package_name not in all_packages:
1333+ return {}
1334+ values = all_packages.get(package_name)
1335+ depends = values.depends.split(', ')
1336+ depends += values.recommends.split(', ') # APT treats recommends as depends
1337+ depends += values.pre_depends.split(', ') # pre-depends are important too
1338+ filename = values.filename.split('/') # Get the parts for the pkg filename
1339+ urls.append((values.filename,
1340+ path_join(directory, filename[-1]),
1341+ values.checksums))
1342+ values.installed_version = values.version
1343+ all_packages[package_name] = values
1344+
1345+ for item in depends:
1346+ data = item.split()
1347+ if not data == []:
1348+ # Get dependencies for this package too if it isn't already
1349+ # installed
1350+ if data[0] in all_packages \
1351+ and not all_packages.get(data[0]).installed_version:
1352+ urls += get_dependencies(directory,
1353+ all_packages,
1354+ data[0])
1355+
1356+ return urls
1357+
1358+
1359+def get_sources(directory):
1360+ """Get the filename of main sources file, relative to directory.
1361+
1362+ :param str directory: the directory holding APT state files
1363+ :return: the path to the sources.list file
1364+ :rtype: str
1365+ """
1366+ return path_join(directory, 'sources', 'sources.list')
1367+
1368+
1369+def install_cache(project_dir, script_name, move=False):
1370+ """Transfer index files to the APT cache. Uses sh script called as 'root'.
1371+
1372+ :param str project_dir: the directory holding the project
1373+ :param str script_name: the name of the script which will place the files
1374+ :param bool move: flags whether files should be moved rather than copied
1375+ :return: flag whether the process was successful
1376+ :rtype: bool
1377+ """
1378+ packs_dir = path_join(project_dir, 'packages')
1379+ # Make sure there are packages to install
1380+ if not os.path.exists(packs_dir):
1381+ log.error(_('%s does not exist: no packages to be installed') %
1382+ packs_dir)
1383+ return False
1384+
1385+ # If we're missing the partial dir, APT will throw a fit.
1386+ partial_dir = path_join(packs_dir, 'partial')
1387+ if not os.path.exists(partial_dir):
1388+ os.mkdir(partial_dir)
1389+
1390+ script_path = path_join(project_dir, '%s.sh' % script_name)
1391+ shutil.copyfile(path_join(config.cwd, 'lib', 'install.sh'), script_path)
1392+
1393+ if move:
1394+ transfer = '-move'
1395+ else:
1396+ transfer = ''
1397+
1398+ log.info(_('install_cache: launching script as root'))
1399+ run = run_root('sh', '%s %s %s' % (script_path, project_dir, transfer))
1400+ if run[0] != 0:
1401+ log.error('exit code:%i\n%s' % (run[0], run[1]))
1402+ log.info(_('install_cache: failed'))
1403+ return False
1404+ log.info(_('install_cache: transfer success'))
1405+ return True
1406+
1407+
1408+def install_packs(project_dir, pack_names):
1409+ """Install the listed packages using downloaded package files in the given
1410+ project directory.
1411+
1412+ :param str project_dir: the directory of the current project
1413+ :param list(str) pack_names: names of the packages to be installed
1414+ :return: a flag whether the process was successful
1415+ :rtype: bool
1416+ """
1417+ packs_dir = path_join(project_dir, 'packages')
1418+ run = run_root('xterm',
1419+ '-e sh -c \"apt-get -y -o '
1420+ 'dir::cache::archives=\\\"%s\\\" '
1421+ '--allow-unauthenticated install %s; echo \\\"'
1422+ 'Press [ENTER] to exit.\\\"; '
1423+ 'read x\"' % (packs_dir, pack_names))
1424+ if run[0] != 0:
1425+ log.error(_('exit code:%i\n%s' % (run[0], run[1])))
1426+ log.info(_('Failed to start Xterm as root'))
1427+ return False
1428+ return True
1429+
1430+
1431+def update_status(out_dir, status_path='/var/lib/dpkg/status'):
1432+ """Update the project's status file (list of installed packages).
1433+
1434+ :param str out_dir: the directory to place the status file in
1435+ :param str status_path: the path to the new status file
1436+ :return: a flag whether the process was successful
1437+ :rtype: bool
1438+ """
1439+ lists_dir = path_join(out_dir, 'lists')
1440+ outfile = path_join(lists_dir, 'status')
1441+ outfile_bak = path_join(lists_dir, 'status.bak')
1442+ # Back up the current status file.
1443+ run_root('cp', '%s %s' % (outfile, outfile_bak))
1444+ # Copy in the new status file.
1445+ run_root('cp', '%s %s' % (status_path, outfile))
1446+ # TODO: check for error from 'cp', return False
1447+ return True # Everything went well.
1448+
1449+
1450+def __read_packages(infile, installed, packages, main_url=''):
1451+ """Read package info from the given index file, include information from the
1452+ list of currently-installed packages, and add it to the current list of
1453+ packages.
1454+
1455+ :param File infile: the opened index file to read package data from
1456+ :param list installed: the list of packages installed on the system
1457+ :param dict packages: the list of already-parsed packages
1458+ :return: packages dictionary with additional package info added
1459+ :rtype: dict
1460+ """
1461+ # Create a new package object. We don't know its name yet.
1462+ current = Package('', checksums={})
1463+
1464+ for line in infile:
1465+ # Excuse how unreadable this next section is. It could be implemented by
1466+ # checking .startswith('Keyname:') for each one, but that's slow. Don't
1467+ # Try to narrow these down further unless you get a full listing of
1468+ # index file key names. False positives abound.
1469+ if line[0] == 'P':
1470+ # Pre-depends
1471+ if line[0:3] == 'Pre':
1472+ current.pre_depends = line[13:-1]
1473+ # Package
1474+ elif line[0:8] == 'Package:':
1475+ current.name = line[9:-1]
1476+ # Version
1477+ elif line[0] == 'V' and line[0:3] == 'Ver':
1478+ current.version = line[9:-1]
1479+ elif line[0] == 'D':
1480+ # Depends
1481+ if line[0:3] == 'Dep':
1482+ current.depends = line[9:-1]
1483+ # Description
1484+ elif line[0:12] == 'Description:':
1485+ current.description = lib.utf(line[13:-1])
1486+ # Filename
1487+ elif line[0] == 'F':
1488+ current.filename = lib.join_url(main_url, line[10:-1])
1489+ elif line[0] == 'S':
1490+ # Size
1491+ if line[1] == 'i':
1492+ current.size = int(line[6:-1])
1493+ # SHA1
1494+ elif line[3] == '1':
1495+ current.checksums['SHA1'] = line[6:-1]
1496+ # SHA256
1497+ elif line[3] == '2':
1498+ current.checksums['SHA256'] = line[8:-1]
1499+ # Recommends
1500+ elif line[0] == 'R' and line[0:3] == 'Rec':
1501+ current.recommends = line[12:-1]
1502+ # MD5sum
1503+ elif line[0] == 'M' and line[1] == 'D':
1504+ current.checksums['MD5sum'] = line[8:-1]
1505+ elif line[0] == '\n' and current.name != '':
1506+ # Finished reading this package, append it
1507+ # Set the packages installed version
1508+ __update_package(current, installed, packages)
1509+ packages[current.name] = current
1510+ current = Package('', checksums={})
1511+
1512+ return packages
1513+
1514+
1515+def __update_package(package, installed, packages):
1516+ """Update package information based on packages already installed.
1517+
1518+ :param Package package: the package to be compared to installed packages
1519+ :param dict installed: the dict of currently installed packages
1520+ :param dict packages: the dict of already-parsed packages
1521+ """
1522+ # Package already exists so update values if need be
1523+ if package.name in packages:
1524+ if version_compare(packages[package.name].version, package.version) \
1525+ == 2:
1526+ return # If it's an older one, skip it
1527+
1528+ # If there's no installed version, move on.
1529+ if package.name not in installed:
1530+ return
1531+
1532+ installed_version = installed[package.name].version
1533+ package.status = version_compare(installed_version, package.version)
1534+ package.installed_version = installed_version
1535+
1536+ packages[package.name] = package # Set package info
1537+
1538+
1539+def __grab_files(directory):
1540+ """Grab files necessary for a Debian project.
1541+
1542+ :param str directory: the directory to place APT files in
1543+ """
1544+ dir_lists = path_join(directory, 'lists')
1545+ dir_sources = path_join(directory, 'sources')
1546+
1547+ shutil.copytree('/etc/apt/', dir_sources)
1548+ try:
1549+ shutil.copytree('/var/lib/apt/lists/', dir_lists)
1550+ except shutil.Error:
1551+ # Certain files in this directory require root. There's no more specific
1552+ # error to catch. If for some reason not all the necessary files get
1553+ # copied, that'll be dealt with in the code that uses them.
1554+ pass
1555+ shutil.copyfile('/var/lib/dpkg/status',
1556+ path_join(dir_lists, 'status'))
1557+ shutil.copyfile('/var/lib/dpkg/status',
1558+ path_join(dir_lists, 'status.bak'))
1559+
1560+
1561+def __parse_sources(directory):
1562+ """Get a list of all deb entries for project.
1563+
1564+ :param str directory: the directory holding source files to be parsed
1565+ :return: a list of deb source lines from source files in the given dir
1566+ :rtype: list
1567+ """
1568+ sources = []
1569+ sources_dir = path_join(directory, 'sources')
1570+
1571+ # Parse main source file
1572+ main_source = path_join(sources_dir, 'sources.list')
1573+ if os.path.exists(main_source):
1574+ sources += __parse_source_file(main_source)
1575+
1576+ # Parse extra source files
1577+ sources_d_dir = path_join(sources_dir, 'sources.list.d')
1578+ if os.path.exists(sources_d_dir):
1579+ for item in os.listdir(sources_d_dir):
1580+ if item.endswith('.list'):
1581+ sources += __parse_source_file(path_join(sources_d_dir, item))
1582+
1583+ return sources
1584+
1585+
1586+def __parse_source_file(location):
1587+ """Get a specific source file's deb (repo/source) entries.
1588+
1589+ :param str location: the path to the source file to be parsed
1590+ :return: a list of deb lines from this file
1591+ :rtype: list
1592+ """
1593+ found = []
1594+ infile = open(location)
1595+ for line in infile:
1596+ # TODO: Add support for more protocols
1597+ if line.startswith('deb http://'):
1598+ if line.endswith('\n'):
1599+ found.append(line[:-1])
1600+ else:
1601+ found.append(line)
1602+ return found
1603+
1604+
1605+def __files_from_debs(deb_list, arch, directory=''):
1606+ """Generates package index filenames based on the given entries from source
1607+ file "deb" entries.
1608+
1609+ :param list deb_list: a list of deb source lines
1610+ :param str arch: either '32bit' or '64bit'
1611+ :param str directory: the directory to prepend to the retrieved filenames
1612+ :return: the file names/paths for the index files related to the given repos
1613+ :rtype: generator
1614+ """
1615+ for line in deb_list:
1616+ source = Source(line, arch)
1617+ for filename in source.filenames:
1618+ path = path_join(directory, filename)
1619+ yield path
1620+
1621+
1622+def __urls_from_debs(deb_list, arch):
1623+ """Return URLs for all index files associated with the repos specified in
1624+ deb_list.
1625+
1626+ :param list deb_list: a list of deb source lines
1627+ :param str arch: either '32bit' or '64bit'
1628+ :return: the URLs for the index files related to the given repos
1629+ :rtype: generator
1630+ """
1631+ for line in deb_list:
1632+ source = Source(line, arch)
1633+ for url in source.urls:
1634+ yield url
1635+
1636+
1637+def __get_installed(directory):
1638+ """Get a list of installed packages.
1639+
1640+ :param str directory: the directory holding package info files
1641+ :return: a dict of installed Packages with package.name as their keys
1642+ :rtype: dict(Package)
1643+ """
1644+ with open(path_join(directory, 'lists', 'status'), 'r') as status:
1645+ installed = {}
1646+ current = Package('')
1647+ for line in status:
1648+ if line.startswith('Package:'):
1649+ current.name = line[9:-1]
1650+ elif line.startswith('Version:'):
1651+ current.version = line[9:-1]
1652+ elif line.startswith('Status:'):
1653+ current.status = line[8:-1]
1654+ elif line.startswith('\n') \
1655+ and current.status == 'install ok installed':
1656+ installed[current.name] = current
1657+ current = Package('')
1658+
1659+ return installed
1660+
1661+
1662+def get_comp_name():
1663+ """Get the name of this computer.
1664+
1665+ :return: the name of this computer
1666+ :rtype: str
1667+ """
1668+ system, node, release, version, machine, processor = platform.uname()
1669+ return system
1670+
1671+
1672+def get_os_name():
1673+ """Get the name of this computers operating system.
1674+
1675+ :return: the computer's OS name
1676+ :rtype: str
1677+ """
1678+ process = subprocess.run(['cat', '/etc/issue.net'], stdout=subprocess.PIPE)
1679+ output = str(process.stdout)
1680+ if process.returncode != 0:
1681+ log.error(_('Problem retrieving Debian version'))
1682+ os_name = output.split()[0]
1683+ return os_name
1684+
1685+
1686+def get_os_version():
1687+ """Get the version of this computer's operating system.
1688+
1689+ :return: the OS version
1690+ :rtype: str
1691+ """
1692+ process = subprocess.run(['cat', '/etc/issue.net'], stdout=subprocess.PIPE)
1693+ output = str(process.stdout)
1694+ if process.returncode != 0:
1695+ log.error(_('Problem retrieving Debian version'))
1696+ os_version = output.split()[1]
1697+ return os_version
1698+
1699+
1700+def get_os_arch():
1701+ """Get the architecture of this computer's operating system. Return value is
1702+ either '32bit' or '64bit'.
1703+
1704+ :return: the OS's architecture
1705+ :rtype: str
1706+ """
1707+ # platform doesn't reliably get the architecture when compiled. So get
1708+ # it more cleverly.
1709+ process = subprocess.run(['apt-get', '-v'], stdout=subprocess.PIPE)
1710+ apt_version = str(process.stdout)
1711+ if 'i386' in apt_version:
1712+ os_arch = '32bit'
1713+ elif 'amd64' in apt_version:
1714+ os_arch = '64bit'
1715+ else:
1716+ log.error(_('Unable to detect architecture. Defaulting to 64-bit. '
1717+ 'If this is incorrect, edit your project\'s '
1718+ 'debian.conf.'))
1719+ os_arch = '64bit'
1720+ return os_arch
1721+
1722+
1723+def get_kernel_name():
1724+ """Get the name of this computer's kernel.
1725+
1726+ :return: the name of the kernel
1727+ :rtype: str
1728+ """
1729+ system, node, release, version, machine, processor = platform.uname()
1730+ return release
1731
1732=== added file 'lib/install.sh'
1733--- lib/install.sh 1970-01-01 00:00:00 +0000
1734+++ lib/install.sh 2017-06-11 16:11:12 +0000
1735@@ -0,0 +1,101 @@
1736+#!/bin/sh -
1737+#
1738+# Keryx 0.92.4 install script
1739+#
1740+# Used to transfer the necessary files back to a computer
1741+# from a project so that the downloaded packages can
1742+# be installed.
1743+#
1744+# Usage <script.sh> [<project directory>] ['-move']
1745+#
1746+# written by jacseen, class=amateur :)
1747+# http://keryxproject.org mailto:keryx@lists.launchpad.net
1748+
1749+if [ -n "$1" ] && [ -e "$1" ]
1750+then
1751+proj="$1"
1752+shift
1753+else
1754+proj="$(pwd)"
1755+fi
1756+cd "$proj"
1757+
1758+if [ -n "$1" ] && [ "$1" = "-move" ]
1759+then
1760+transfer="mov"
1761+else
1762+transfer="copy"
1763+fi
1764+
1765+slists=lists
1766+tlists=/var/lib/apt/lists
1767+#spacks=packages
1768+#tpacks=/var/cache/apt/archives
1769+ssources=sources
1770+tsources=/etc/apt
1771+
1772+#Find all index files, skipping the 'status' files and copy them
1773+cd ./"$slists"/
1774+if [ ! $? = 0 ] #if cannot cd into lists, not project dir
1775+then
1776+echo "Not project dir: $(pwd)"
1777+exit 65
1778+fi
1779+filelist=`find -maxdepth 1 -iname '*_dists_*'`
1780+
1781+#TODO:attain lock on folder $tlists to be package-manager-friendly
1782+# will be attained directly with python in later versions
1783+
1784+for fn in $filelist
1785+do
1786+cp -t "$tlists" "$fn"
1787+if [ ! $? = 0 ]
1788+then
1789+ echo "Failure when copying list: $fn"
1790+ exit 66
1791+fi
1792+done
1793+
1794+#TODO:release lock on folder $tlists
1795+
1796+### Debs will no longer be moved to the cache. The downloads directory will ###
1797+### Be made Temporary cache. ###
1798+
1799+#Find all downloaded packages and move to cache
1800+#cd ../"$spacks"/
1801+#filelist=`find -maxdepth 1 -name '*.deb'`
1802+
1803+#TODO:attain lock on folder $tpacks to be package-manager-friendly
1804+# will be attained directly with python in later versions
1805+
1806+#for fn in $filelist
1807+#do
1808+# if [ $transfer = "mov" ]
1809+# then
1810+# mv -f -t "$tpacks" "$fn"
1811+# else
1812+# cp -t "$tpacks" "$fn"
1813+# fi
1814+# if [ ! $? = 0 ]
1815+# then
1816+# echo "Failure when ${transfer}ing package: $fn"
1817+# exit 67
1818+# fi
1819+#done
1820+
1821+#TODO:release lock on folder $tpacks
1822+
1823+#Update the main sources.list file in case it was changed in keryx
1824+
1825+cd ../"$ssources"/
1826+cp -t "$tsources" "sources.list"
1827+if [ ! $? = 0 ]
1828+then
1829+echo "Failure when copying sources.list"
1830+exit 68
1831+fi
1832+
1833+# Update the APT caches with the latest lists
1834+apt-cache gencaches
1835+
1836+exit 0
1837\ No newline at end of file
1838
1839=== modified file 'lib/log.py'
1840--- lib/log.py 2010-02-20 04:17:16 +0000
1841+++ lib/log.py 2017-06-11 16:11:12 +0000
1842@@ -16,36 +16,40 @@
1843 # along with this program; if not, write to the Free Software
1844 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1845
1846-import consts
1847+from . import config
1848 import logging
1849 import os
1850-import sys
1851
1852 from logging.handlers import RotatingFileHandler
1853
1854 # Make sure the directory exists
1855-if not os.path.exists(consts.dirLog):
1856- os.mkdir(consts.dirLog)
1857-
1858-try:
1859- __logHandler = RotatingFileHandler(consts.fileLog, maxBytes=0, backupCount=2)
1860- __logHandler.doRollover()
1861- __logHandler.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)-7s %(message)s', datefmt='%y-%m-%d %H:%M:%S'))
1862-
1863- logger = logging.getLogger(consts.appNameShort)
1864- logger.setLevel(logging.INFO)
1865- logger.addHandler(__logHandler)
1866-except:
1867- print 'Unable to start Keryx'
1868- print 'Make sure no other instances of Keryx are still running.'
1869- sys.exit(1)
1870+if not os.path.exists(config.log_dir):
1871+ os.mkdir(config.log_dir)
1872+
1873+__logHandler = RotatingFileHandler(config.log_path, maxBytes=0, backupCount=2)
1874+__logHandler.doRollover()
1875+__logHandler.setFormatter(
1876+ logging.Formatter('[%(asctime)s] %(levelname)-7s %(message)s',
1877+ datefmt='%y-%m-%d %H:%M:%S'))
1878+
1879+logger = logging.getLogger(config.name.lower())
1880+logger.setLevel(logging.INFO)
1881+logger.addHandler(__logHandler)
1882+
1883
1884 def info(status):
1885- """Prints status to log file and to console"""
1886+ """Print status to log file and to console.
1887+
1888+ :param str status: the status to be logged
1889+ """
1890 logger.info(status)
1891- print status
1892+ print(status)
1893+
1894
1895 def error(status):
1896- """Prints error to log file and to console"""
1897+ """Print error to log file and to console.
1898+
1899+ :param str status: the status to be logged
1900+ """
1901 logger.error(status)
1902- print status
1903+ print(status)
1904
1905=== added file 'lib/package.py'
1906--- lib/package.py 1970-01-01 00:00:00 +0000
1907+++ lib/package.py 2017-06-11 16:11:12 +0000
1908@@ -0,0 +1,43 @@
1909+class Package:
1910+ """Represents a package in the APT system, as described in the lists from
1911+ some package repository.
1912+ """
1913+ def __init__(self,
1914+ name,
1915+ version='',
1916+ description='',
1917+ filename='',
1918+ size='',
1919+ depends='',
1920+ recommends='',
1921+ pre_depends='',
1922+ checksums=None,
1923+ status='',
1924+ installed_version=''):
1925+ """Sets the initial values for this Package.
1926+
1927+ :param str name: the name of this package
1928+ :param str version: the version of this package
1929+ :param str description: the description of this package
1930+ :param str filename: the filename of this package
1931+ :param str size: the size of this package
1932+ :param list depends: a list of packages this one depends on
1933+ :type depends: None or list
1934+ :param recommends: a list of packages this one recommends
1935+ :type recommends: None or list
1936+ :param pre_depends: a list of pre-dependencies for this package
1937+ :type pre_depends: None or list
1938+ :param checksums: a dictionary of algorithm: checksum pairs
1939+ :type checksums: None or dict
1940+ """
1941+ self.name = name
1942+ self.version = version
1943+ self.description = description
1944+ self.filename = filename
1945+ self.size = size
1946+ self.depends = depends
1947+ self.recommends = recommends
1948+ self.pre_depends = pre_depends
1949+ self.checksums = checksums
1950+ self.status = status
1951+ self.installed_version = installed_version
1952
1953=== removed file 'lib/plugins.py'
1954--- lib/plugins.py 2010-02-20 04:17:16 +0000
1955+++ lib/plugins.py 1970-01-01 00:00:00 +0000
1956@@ -1,88 +0,0 @@
1957-# -*- coding: utf-8 -*-
1958-#
1959-# Author: Chris Oliver (excid3@gmail.com)
1960-#
1961-# This program is free software; you can redistribute it and/or modify
1962-# it under the terms of the GNU General Public License as published by
1963-# the Free Software Foundation; either version 2 of the License, or
1964-# (at your option) any later version.
1965-#
1966-# This program is distributed in the hope that it will be useful,
1967-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1968-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1969-# GNU Library General Public License for more details.
1970-#
1971-# You should have received a copy of the GNU General Public License
1972-# along with this program; if not, write to the Free Software
1973-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1974-
1975-import consts
1976-import os.path
1977-import re
1978-import sys
1979-
1980-import log
1981-
1982-OSPluginList = []
1983-InterfacePluginList = []
1984-app = None
1985-fail = False
1986-
1987-class pluginBase(object):
1988- """Used to make sure plugins have necessary functions"""
1989- def __init__(self): pass
1990- def IsOS(self): pass
1991- def cleanup(self): pass
1992-
1993- # Interface plugins
1994- def getWidgets(self, instance): self.app = instance
1995-
1996- # OS Plugins
1997- def createProject(self): pass
1998- def loadProject(self): pass
1999- def loadLocalPackageList(self): pass
2000- def loadInternetPackageList(self): pass
2001- def getDependencies(self): pass
2002- def getSources(self): pass
2003- def installCache(self): pass
2004- def installRepo(self): pass
2005-
2006-
2007-def load(dir, instance, load_interface_plugins=True):
2008- """Enumerates a list of plugins in a directory"""
2009- app = instance
2010-
2011- # Make sure dir exists
2012- if not os.path.isdir(dir):
2013- log.info(_('Cannot find ') + consts.dirPlugins + _(', exiting.'))
2014- return
2015-
2016- # Try to load each file in plugins dir
2017- sys.path.insert(0, dir)
2018- for file in os.listdir(dir):
2019- if file.endswith('.py'):
2020- status = initialize(file)
2021- if status != None: # Plugin loaded correctly
2022- status[1].getWidgets(instance) # Give all interface plugins widget tree
2023- # FIXME: Give plugins that have the attribute set only
2024- if status[2] == 'OS': OSPluginList.append(status)
2025- elif status[2] == 'Interface' and load_interface_plugins:
2026- #status[1].getWidgets(instance) # Give all interface plugins widget tree
2027- InterfacePluginList.append(status)
2028-
2029- if fail: log.info(_('Plugin(s) have failed to load. This may be due to not having python-wxversion installed.'))
2030-
2031-def initialize(file):
2032- """Initializes plugin given filename and returns its instance"""
2033- try:
2034- plugin = __import__(re.sub('\.pyc?$', '', file))
2035- if not hasattr(plugin, 'PLUGIN_NAME'): return
2036- name = getattr(plugin, 'PLUGIN_NAME')
2037- instance = getattr(plugin, name)()
2038- ptype = getattr(plugin, 'PLUGIN_TYPE')
2039- version = getattr(plugin, 'PLUGIN_VERSION')
2040- log.info(_('Plugin loaded: ') + file[:-3] + ' v' + version)
2041- return [name, instance, ptype, version]
2042- except Exception, e:
2043- log.error(file + _(' failed to load.'))
2044- log.error(e)
2045
2046=== modified file 'lib/project.py'
2047--- lib/project.py 2010-02-20 04:17:16 +0000
2048+++ lib/project.py 2017-06-11 16:11:12 +0000
2049@@ -17,104 +17,114 @@
2050 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2051
2052 import os
2053-from threading import Thread
2054-
2055-import consts
2056-import log
2057-import plugins
2058+
2059+import time
2060+
2061+from lib import debian
2062+from . import config
2063+from . import log
2064
2065 # Make sure the directory exists
2066-if not os.path.exists(consts.dirProjects):
2067- os.mkdir(consts.dirProjects)
2068-
2069-# List of currently opened projects
2070-projects = []
2071+if not os.path.exists(config.projects_dir):
2072+ os.mkdir(config.projects_dir)
2073+
2074+# The currently-opened project
2075+current = None
2076+
2077+
2078+class ProjectExistsException(Exception):
2079+ pass
2080+
2081+
2082+def create_project(project_name):
2083+ """Create a project with the given name and plugin."""
2084+ # Get the full path of the new project's directory
2085+ project_dir = os.path.join(config.projects_dir, project_name)
2086+
2087+ # Make sure a project by that name doesn't already exist
2088+ if os.path.exists(project_dir):
2089+ raise ProjectExistsException
2090+
2091+ # Write the basic project information to the project directory
2092+ os.mkdir(project_dir)
2093+
2094+ # Write the remaining project information to the project file
2095+ debian.create_project(project_name)
2096+
2097+ log.info('Created project: ' + project_name)
2098+
2099
2100 class Project(object):
2101 """Stores project information and common tasks"""
2102- def __init__(self, name="", dir="", plugin="", comp="", os="", ver="", arch="", kernel=""):
2103+ def __init__(self,
2104+ name='',
2105+ directory='',
2106+ plugin=None,
2107+ comp='',
2108+ os_name='',
2109+ ver='',
2110+ arch='',
2111+ kernel=''):
2112 self.name = name
2113- self.dir = dir
2114+ self.dir = directory
2115 self.plugin = plugin
2116- self.computername = comp
2117- self.os = os
2118- self.version = ver
2119- self.architecture = arch
2120- self.kernel = kernel
2121+ self.comp_name = comp
2122+ self.os_name = os_name
2123+ self.os_version = ver
2124+ self.os_arch = arch
2125+ self.kernel_name = kernel
2126 self.packages = {}
2127
2128- def GetData(self):
2129- return [self.name, self.dir, self.os, self.version, self.architecture, self.kernel]
2130+ def get_data(self):
2131+ return [self.name,
2132+ self.dir,
2133+ self.os_name,
2134+ self.os_version,
2135+ self.os_arch,
2136+ self.kernel_name]
2137
2138- def SetData(self, name="", dir="", plugin="", comp="", os="", ver="", arch="", kernel=""):
2139+ def set_data(self,
2140+ name='',
2141+ directory='',
2142+ plugin=None,
2143+ comp='',
2144+ os_name='',
2145+ ver='',
2146+ arch='',
2147+ kernel=''):
2148 self.name = name
2149- self.dir = dir
2150+ self.dir = directory
2151 self.plugin = plugin
2152- self.computername = comp
2153- self.os = os
2154- self.version = ver
2155- self.architecture = arch
2156- self.kernel = kernel
2157-
2158-
2159-
2160- """Project manipulation functions"""
2161-
2162- def CreateKeryx(self, name, pluginName, plugin):
2163- """Creates a project"""
2164- try:
2165- #if 1:
2166- dirProj = os.path.join(consts.dirProjects, name)
2167- if os.path.exists(dirProj): return False, '' # False, '' means that files were changed
2168- filename = os.path.join(dirProj, name) + consts.appFileExt
2169- os.mkdir(dirProj)
2170- projFile = open(filename, 'wb')
2171- projFile.write(name + '\n' + pluginName)
2172- projFile.close()
2173- log.info('Created project: ' + name)
2174- plugin.createProject(name) #Create plugin information
2175- return True, filename
2176- except:
2177- return False, name
2178-
2179- def OpenKeryx(self, name):
2180+ self.comp_name = comp
2181+ self.os_name = os_name
2182+ self.os_version = ver
2183+ self.os_arch = arch
2184+ self.kernel_name = kernel
2185+
2186+ def open_project(self, name):
2187 """Opens a project"""
2188- try:
2189- infile = open(name, 'rb')
2190- data = infile.read()
2191- infile.close()
2192- data = data.split('\n')
2193-
2194- self.name = data[0]
2195- self.dir = os.path.dirname(name)
2196- self.plugin_name = data[1]
2197-
2198- #Initialize plugin
2199- for item in plugins.OSPluginList:
2200- if item[0] == data[1]:
2201- self.plugin = item[1] # Set plugin to correct one
2202- info = self.plugin.loadProject(self.dir)
2203-
2204- self.computername = info[0]
2205- self.os = info[1]
2206- self.version = info[2]
2207- self.architecture = info[3]
2208- self.kernel = info[4]
2209-
2210- return True
2211- except:
2212- return False
2213-
2214- def getDependencies(self, package):
2215+ self.name = name
2216+ self.dir = os.path.join(config.projects_dir, name)
2217+
2218+ self.comp_name = debian.get_comp_name()
2219+ self.os_name = debian.get_os_name()
2220+ self.os_version = debian.get_os_version()
2221+ self.os_arch = debian.get_os_arch()
2222+ self.kernel_name = debian.get_kernel_name()
2223+
2224+ def get_dependencies(self, package):
2225 """Gets dependencies for package"""
2226- return self.plugin.getDependencies(os.path.join(self.dir, 'packages'), self.packages, package)
2227-
2228- def getUrls(self):
2229- return self.plugin.loadInternetPackageList(self.dir, self.architecture)
2230-
2231- def loadLocal(self, jobID, abortEvent):
2232- self.packages = self.plugin.loadLocalPackageList(self.dir, self.architecture)
2233+ return debian.get_dependencies(os.path.join(self.dir, 'packages'),
2234+ self.packages,
2235+ package)
2236+
2237+ def get_urls(self):
2238+ return debian.load_remote_package_list(self.dir, self.os_arch)
2239+
2240+ def load_local(self, jobID, abortEvent):
2241+ self.packages = debian.load_local_package_list(self.dir, self.os_arch)
2242+
2243 return True
2244
2245- def getSources(self):
2246- return self.plugin.getSources(self.dir)
2247+ def get_sources(self):
2248+ return debian.get_sources(self.dir)
2249
2250=== added file 'lib/source.py'
2251--- lib/source.py 1970-01-01 00:00:00 +0000
2252+++ lib/source.py 2017-06-11 16:11:12 +0000
2253@@ -0,0 +1,41 @@
2254+from lib import join_url
2255+
2256+
2257+class Source:
2258+ """Represents a line from an APT sources file."""
2259+ def __init__(self, line, arch):
2260+ """Sets up the source's information based on the given deb line.
2261+
2262+ :param str line: a line from an APT sources file
2263+ :param str arch: either '32bit' or '64bit'
2264+ """
2265+ self.line = line
2266+ self.arch = arch
2267+ self.urls = self.__get_urls()
2268+ self.filenames = self.__get_filenames()
2269+
2270+ def __get_urls(self):
2271+ """Get the URLs of index files associated with this source."""
2272+ parts = self.line.split()
2273+ repo_url = parts[1]
2274+ dist = parts[2]
2275+ for section in parts[3:]:
2276+ url = join_url(repo_url, 'dists', dist, section)
2277+
2278+ if self.arch == '32bit':
2279+ url = join_url(url, 'binary-i386/Packages.gz')
2280+ elif self.arch == '64bit':
2281+ url = join_url(url, 'binary-amd64/Packages.gz')
2282+
2283+ yield url
2284+
2285+ def __get_filenames(self):
2286+ """Get a list of filenames for index files in this repo. It's the path
2287+ to the file on the server with slashes replaced by underscores and the
2288+ .gz file extension removed.
2289+ """
2290+ for url in self.urls:
2291+ # Simply remove the protocol designator and replace slashes with
2292+ # underscores.
2293+ path = url.split('//')[1]
2294+ yield path.replace('/', '_')
2295
2296=== added directory 'lib/tests'
2297=== added file 'lib/tests/__init__.py'
2298=== added file 'lib/tests/test_source.py'
2299--- lib/tests/test_source.py 1970-01-01 00:00:00 +0000
2300+++ lib/tests/test_source.py 2017-06-11 16:11:12 +0000
2301@@ -0,0 +1,14 @@
2302+from unittest import TestCase
2303+from lib.source import Source
2304+
2305+
2306+class TestSource(TestCase):
2307+ def test_urls(self):
2308+ source = Source('deb http://security.ubuntu.com/ubuntu xenial-security '
2309+ 'main restricted',
2310+ '64bit')
2311+
2312+ # Make sure the class knows there are two URLs associated with that line
2313+ self.assertEqual(sum(1 for _ in source.urls), 2)
2314+
2315+ # TODO: test more things
2316
2317=== added file 'lib/version.py'
2318--- lib/version.py 1970-01-01 00:00:00 +0000
2319+++ lib/version.py 2017-06-11 16:11:12 +0000
2320@@ -0,0 +1,207 @@
2321+def version_compare(str1, str2):
2322+ ver1 = Version(str1)
2323+ ver2 = Version(str2)
2324+
2325+ ver_type = ('epoch', 'upstream', 'debian')
2326+ test_type = (0, 2, 1)
2327+
2328+ parse1 = []
2329+ parse2 = []
2330+
2331+ version_list1 = (ver1.epoch(), ver1.upstream(), ver1.debian_version())
2332+ version_list2 = (ver2.epoch(), ver2.upstream(), ver2.debian_version())
2333+
2334+ for index in range(3):
2335+ equal = __compare_info(version_list1[index], version_list2[index], ver_type[index])
2336+ if equal != 0:
2337+ break
2338+
2339+ return equal
2340+
2341+
2342+def __compare_info(string1, string2, ver_type):
2343+ equal = 0
2344+ if string1 and string2: # Both strings contain a value
2345+ parse1 = parse_version(string1)
2346+ parse2 = parse_version(string2)
2347+
2348+ max = len(parse1)
2349+ if len(parse1) > len(parse2):
2350+ max = len(parse2)
2351+ # Go through the parsed list of the version number, broken down by
2352+ # type
2353+ for count in range(max):
2354+ compare1 = parse1[count][1]
2355+ type1 = parse1[count][0]
2356+ compare2 = parse2[count][1]
2357+ type2 = parse2[count][0]
2358+ if type1 == 'alpha' and type2 == 'alpha' or \
2359+ type1 == 'num' and type2 == 'num' or \
2360+ type1 == 'delimit' and type2 == 'delimit':
2361+ if compare1 < compare2:
2362+ equal = 1
2363+ break
2364+ elif compare1 > compare2:
2365+ equal = 2
2366+ break
2367+ elif type1 == 'alpha' and type2 == 'num' or \
2368+ type1 == 'alpha' and type2 == 'delimit' or \
2369+ type1 == 'alpha' and type2 == 'tilde' or \
2370+ type1 == 'num' and type2 == 'tilde' or \
2371+ type1 == 'delimit' and type2 == 'num' or \
2372+ type1 == 'delimit' and type2 == 'tilde':
2373+ equal = 2
2374+ break
2375+ elif type1 == 'num' and type2 == 'alpha' or \
2376+ type1 == 'num' and type2 == 'delimit' or \
2377+ type1 == 'tilde' and type2 == 'alpha' or \
2378+ type1 == 'tilde' and type2 == 'num' or \
2379+ type1 == 'tilde' and type2 == 'delimit' or \
2380+ type1 == 'delimit' and type2 == 'alpha':
2381+ equal = 1
2382+ break
2383+ elif type1 == 'tilde' and type2 == 'tilde':
2384+ # The more tilde characters, the less its value
2385+ if len(str(compare1)) > len(str(compare2)):
2386+ equal = 1
2387+ break
2388+ elif len(str(compare1)) < len(str(compare2)):
2389+ equal = 2
2390+ break
2391+ if equal == 0:
2392+ if len(parse1) > max:
2393+ compare1 = parse1[max][1]
2394+ type1 = parse1[max][0]
2395+# print compare1,
2396+# print type1
2397+ if type1 == 'num' or \
2398+ type1 == 'alpha' or \
2399+ type1 == 'delimit':
2400+ equal = 2
2401+ elif type1 == 'tilde':
2402+ equal = 1
2403+ elif len(parse2) > max:
2404+ compare2 = parse2[max][1]
2405+ type2 = parse2[max][0]
2406+ if type2 == 'num' or \
2407+ type2 == 'alpha' or \
2408+ type2 == 'delimit':
2409+ equal = 1
2410+ elif type1 == 'tilde':
2411+ equal = 2
2412+
2413+ elif ver_type == 'epoch' and string1 and not string2 or \
2414+ ver_type == 'debian' and not string1 and string2:
2415+ equal = 1
2416+ elif ver_type == 'debian' and string1 and not string2 or \
2417+ ver_type == 'epoch' and not string1 and string2:
2418+ equal = 2
2419+
2420+ return equal
2421+
2422+
2423+def parse_version(version):
2424+ version_list = []
2425+
2426+ # Tracks the current string type (num, alpha, delimit, tilde)
2427+ build_type = ''
2428+ string_val = ''
2429+
2430+ for char in version:
2431+ if __type_changed(build_type, char):
2432+ # If the build type is not empty (there is a string ready to be
2433+ # appended)
2434+ if build_type:
2435+ if string_val.isdigit():
2436+ # Convert string to integer
2437+ version_list.append([build_type, int(string_val)])
2438+ string_val = ''
2439+ else:
2440+ version_list.append([build_type, string_val])
2441+ string_val = ''
2442+ build_type = __get_type(char)
2443+ string_val += char
2444+
2445+ if build_type:
2446+ if string_val.isdigit():
2447+ version_list.append([build_type, int(string_val)])
2448+ else:
2449+ version_list.append([build_type, string_val])
2450+
2451+ return version_list # returns a list of [build_type, string_val]
2452+
2453+
2454+def __type_changed(build_type, char):
2455+ ret_val = False
2456+
2457+ if (char.isdigit() and build_type != 'num') or \
2458+ (char.isalpha() and build_type != 'alpha') or \
2459+ (build_type == 'num' and not char.isdigit()) or \
2460+ (build_type == 'alpha' and not char.isalpha()) or \
2461+ (build_type == 'tilde' and char != '~') or \
2462+ (build_type == 'delimit' and (char == '~' or char.isdigit() or char.isalpha())):
2463+ ret_val = True
2464+
2465+ return ret_val
2466+
2467+
2468+def __get_type(char):
2469+ ret_type = ''
2470+ if char.isalpha():
2471+ ret_type = 'alpha'
2472+ elif char.isdigit():
2473+ ret_type = 'num'
2474+ elif char == '~':
2475+ ret_type = 'tilde'
2476+ else:
2477+ ret_type = 'delimit'
2478+
2479+ return ret_type
2480+
2481+
2482+class Version:
2483+ """This class represents the version of a package and provides certain tools
2484+ to learn information about that version.
2485+ """
2486+ def __init__(self, ver_string):
2487+ self.version = ver_string
2488+ self.epoch_value = self.__get_epoch(self.version)
2489+ upstream_deb = self.__get_upstrdeb(self.version)
2490+ self.upstream_value = upstream_deb[0]
2491+ self.debver_value = upstream_deb[1]
2492+
2493+ def epoch(self):
2494+ return self.epoch_value
2495+
2496+ def upstream(self):
2497+ return self.upstream_value
2498+
2499+ def debian_version(self):
2500+ return self.debver_value
2501+
2502+ def __get_epoch(self, str1):
2503+ ret_epoch = []
2504+ epoch = str1.split(':')
2505+ if len(epoch) > 1:
2506+ ret_epoch = epoch[0]
2507+
2508+ return ret_epoch
2509+
2510+ def __get_upstrdeb(self, str1):
2511+ ver_list = []
2512+ version = str1.split('-')
2513+
2514+ debver = ''
2515+ upstream = ''
2516+
2517+ if len(version) > 1:
2518+ debver = version[len(version) - 1]
2519+ for index in range(len(version) - 1):
2520+ if index == 0:
2521+ upstream = version[index]
2522+ else:
2523+ upstream += '-' + version[index]
2524+ else:
2525+ upstream = str1
2526+
2527+ return [upstream, debver]
2528
2529=== modified file 'lib/wxkeryx/__init__.py'
2530--- lib/wxkeryx/__init__.py 2010-02-20 04:17:16 +0000
2531+++ lib/wxkeryx/__init__.py 2017-06-11 16:11:12 +0000
2532@@ -16,34 +16,33 @@
2533 # along with this program; if not, write to the Free Software
2534 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2535
2536-import wx
2537-
2538-from lib import consts, log, project
2539-from main import MainApp
2540-from startDialog import startDialog
2541-
2542-class wxKeryx(wx.App):
2543+import wx
2544+from gettext import gettext as _
2545+
2546+from lib import config, log, project
2547+from .main import MainApp
2548+from .startDialog import StartDialog
2549+
2550+
2551+class WxKeryx(wx.App):
2552 def OnInit(self):
2553 log.info(_('wxWidgets interface loaded'))
2554- wx.InitAllImageHandlers()
2555- main = MainApp(None, -1, consts.appName + " v" + consts.appVersion)
2556+ main = MainApp(None, -1, config.name + ' v' + config.version)
2557 self.SetTopWindow(main)
2558 main.Show()
2559 main.SetFocus()
2560
2561- start = startDialog(None, -1, '')
2562+ start = StartDialog()
2563 success = start.ShowModal()
2564 start.Destroy()
2565
2566- if success == wx.ID_CANCEL: main.Close()
2567+ if success == wx.ID_CANCEL:
2568+ main.Close()
2569 else:
2570- main.Refresh(project.projects[len(project.projects) - 1].GetData())
2571+ main.Refresh(project.current.get_data())
2572 self.MainLoop()
2573 return 1
2574
2575-# end of class wxKeryx
2576
2577-def Start():
2578-# import gettext
2579-# gettext.install(consts.appNameShort)
2580- keryx = wxKeryx(0)
2581+def start():
2582+ WxKeryx(0)
2583
2584=== modified file 'lib/wxkeryx/delayedresult.py'
2585--- lib/wxkeryx/delayedresult.py 2010-02-20 04:17:16 +0000
2586+++ lib/wxkeryx/delayedresult.py 2017-06-11 16:11:12 +0000
2587@@ -1,11 +1,14 @@
2588-import os.path
2589+import os.path
2590+import urllib
2591 import wx
2592-import wx.lib.delayedresult as delayedresult
2593-
2594-import lib
2595-from lib import consts, log
2596-
2597-class thread(wx.Frame):
2598+import wx.lib.delayedresult as delayedresult
2599+from gettext import gettext as _
2600+
2601+from lib import log
2602+from lib import config
2603+
2604+
2605+class Thread(wx.Frame):
2606 """This demos simplistic use of delayedresult module."""
2607 def __init__(self, parent, func, endfunc):
2608 wx.Frame.__init__(self, None, title=_("Loading..."))
2609@@ -13,81 +16,64 @@
2610 self.function = func
2611 self.endfunction = endfunc
2612
2613- self.SetIcon(wx.Icon(consts.fileIco, wx.BITMAP_TYPE_ICO))
2614+ self.SetIcon(wx.Icon(config.ico_path, wx.BITMAP_TYPE_ICO))
2615 panel = wx.Panel(self)
2616- loading = wx.StaticText(panel, -1, _("This may take a while. Please be patient."))
2617+ loading = wx.StaticText(panel, -1, _("This may take a while. Please be "
2618+ "patient."))
2619 self.gauge = wx.Gauge(panel)
2620- #cancelBtn = wx.Button(panel, -1, "Cancel")
2621-
2622- #self.cur = wx.StaticText(panel, -1, "Current Transfer:")
2623- #self.cur.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
2624- #self.current = wx.StaticText(panel)
2625- #self.download_gauge = wx.Gauge(panel)
2626- #self.status = wx.TextCtrl(panel, style=wx.TE_MULTILINE|wx.TE_READONLY)
2627-
2628- #status = wx.BoxSizer()
2629- #status.Add(self.cur, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
2630- #status.Add(self.current, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
2631
2632 main = wx.BoxSizer(wx.VERTICAL)
2633- main.Add(loading, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5)
2634- main.Add(self.gauge, 0, wx.EXPAND|wx.ALL, 5)
2635- #main.Add(status)
2636- #main.Add(self.download_gauge, 0, wx.EXPAND|wx.ALL, 5)
2637- #main.Add(self.status, 1, wx.EXPAND|wx.ALL, 5)
2638- #main.Add(cancelBtn, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
2639+ main.Add(loading, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 5)
2640+ main.Add(self.gauge, 0, wx.EXPAND | wx.ALL, 5)
2641
2642 panel.SetSizer(main)
2643- self.SetSize(wx.Size(300,100))
2644+ self.SetSize(wx.Size(300, 100))
2645 self.Center()
2646 self.Show()
2647
2648 self.jobID = 0
2649 self.abortEvent = delayedresult.AbortEvent()
2650- self.Bind(wx.EVT_CLOSE, self.OnClose)
2651- #self.Bind(wx.EVT_BUTTON, self.handleAbort, cancelBtn)
2652- self.Bind(wx.EVT_TIMER, self.TimerHandler)
2653+ self.Bind(wx.EVT_CLOSE, self.on_close)
2654+ self.Bind(wx.EVT_TIMER, self.timer_handler)
2655
2656 self.timer = wx.Timer(self)
2657 self.timer.Start(100)
2658
2659 self.parent.Enable(False)
2660- self.handleGet(None)
2661+ self.handle_get(None)
2662
2663- def TimerHandler(self, event):
2664+ def timer_handler(self, event):
2665 self.gauge.Pulse()
2666
2667- def OnClose(self, event):
2668+ def on_close(self, event):
2669 """Only needed because in demo, closing the window does not kill the
2670 app, so worker thread continues and sends result to dead frame; normally
2671- your app would exit so this would not happen."""
2672+ your app would exit so this would not happen.
2673+ """
2674 #if self.buttonAbort.IsEnabled():
2675 #self.log( "Exiting: Aborting job %s" % self.jobID )
2676- #self.handleAbort(None)
2677+ #self.handle_abort(None)
2678 self.Show()
2679
2680- def handleGet(self, event):
2681+ def handle_get(self, event):
2682 """Compute result in separate thread, doesn't affect GUI response."""
2683- #self.buttonGet.Enable(False)
2684- #self.buttonAbort.Enable(True)
2685 self.abortEvent.clear()
2686 self.jobID += 1
2687
2688- #log.info( "Starting job %s in producer thread: GUI remains responsive"
2689- # % self.jobID )
2690- delayedresult.startWorker(self._resultConsumer, self.function,
2691- wargs=(self.jobID,self.abortEvent), jobID=self.jobID)
2692+ delayedresult.startWorker(self._result_consumer, self.function,
2693+ wargs=(self.jobID, self.abortEvent),
2694+ jobID=self.jobID)
2695
2696-
2697- def _resultProducer(self, jobID, abortEvent):
2698+ def _result_producer(self, job_id, abort_event):
2699 """Downloads the files in self.files"""
2700
2701 msg = "Downloading %i file(s)\n" % len(self.files)
2702- wx.CallAfter(self.LogMessage, msg)
2703+ wx.CallAfter(self.log_message, msg)
2704
2705 success = True
2706 for data in self.files:
2707- if abortEvent(): return [1, self.files]
2708+ if abort_event():
2709+ return [1, self.files]
2710
2711 url = data[0]
2712 file = data[1]
2713@@ -97,61 +83,63 @@
2714 end = end[len(end) - 1]
2715
2716 msg = "%s%s %s" % (protocol, site, end)
2717- wx.CallAfter(self.SetFile, msg)
2718+ wx.CallAfter(self.set_file, msg)
2719
2720- try: # Attempt to download the file
2721+ try: # Attempt to download the file
2722 urllib.urlretrieve(url, file, self.progress)
2723- #TODO: Generate md5hashes for these files
2724+ # TODO: Generate md5hashes for these files
2725
2726 msg = "Success: %s\nSaved to: %s\n" % (url, file)
2727- wx.CallAfter(self.LogMessage, msg)
2728+ wx.CallAfter(self.log_message, msg)
2729
2730- except IOError, e: # Failed downloading
2731+ except IOError as e: # Failed downloading
2732 success = False
2733 msg = "Failed: %s\nReason: %s\n" % (url, str(e))
2734- wx.CallAfter(self.LogMessage, msg)
2735+ wx.CallAfter(self.log_message, msg)
2736
2737- if success == True:
2738- wx.CallAfter(self.DisplayMessage,_("All downloads have been completed successfully."), _("Download Complete"))
2739- result = [0,self.files]
2740+ if success:
2741+ wx.CallAfter(self.display_message, _("All downloads have been "
2742+ "completed successfully."),
2743+ _("Download Complete"))
2744+ result = [0, self.files]
2745 else:
2746- wx.CallAfter(self.DisplayMessage, _("Some downloads failed to complete.") + "\n" +_("Please check") + " " + os.path.join(consts.dirLog, "log") + " " + _("for more details."), _("Download Failed"))
2747- result = [1,self.files]
2748+ wx.CallAfter(self.display_message, _("Some downloads failed to complete.") + "\n" + _("Please check") + " " + config.log_path + " " + _("for more details."), _("Download Failed"))
2749+ result = [1, self.files]
2750 return result
2751
2752 def progress(self, blocks, size, total):
2753- if blocks*size > total: fraction = float(total)/float(total)
2754- else: fraction = float(blocks*size)/float(total)
2755+ if blocks*size > total:
2756+ fraction = float(total)/float(total)
2757+ else:
2758+ fraction = float(blocks*size)/float(total)
2759
2760- wx.CallAfter(self.SetGauge, int(round(fraction*100,2)))
2761+ wx.CallAfter(self.set_gauge, int(round(fraction * 100, 2)))
2762
2763- def LogMessage(self, msg):
2764+ def log_message(self, msg):
2765 log.info(msg)
2766 self.status.AppendText(msg)
2767- def DisplayMessage(self, msg, caption):
2768+
2769+ def display_message(self, msg, caption):
2770 wx.MessageBox(msg, caption)
2771- def SetGauge(self, val):
2772+
2773+ def set_gauge(self, val):
2774 self.download_gauge.SetValue(val)
2775- def SetFile(self, val):
2776+
2777+ def set_file(self, val):
2778 self.current.SetLabel(val)
2779
2780- def handleAbort(self, event):
2781+ def handle_abort(self, event):
2782 """Abort the result computation."""
2783- log.info( "Aborting result for job %s" % self.jobID )
2784+ log.info("Aborting result for job %s" % self.jobID)
2785 #self.buttonGet.Enable(True)
2786 #self.buttonAbort.Enable(False)
2787 self.abortEvent.set()
2788
2789-
2790- def _resultConsumer(self, delayedResult):
2791- jobID = delayedResult.getJobID()
2792+ def _result_consumer(self, delayed_result):
2793+ jobID = delayed_result.getJobID()
2794 assert jobID == self.jobID
2795- try:
2796- result = delayedResult.get()
2797- except Exception, exc:
2798- log.info( "Result for job %s raised exception: %s" % (jobID, exc) )
2799- return
2800-
2801+ result = delayed_result.get()
2802+
2803 # output result
2804 #log.info( "Got result for job %s: %s" % (jobID, result) )
2805 self.parent.Enable()
2806
2807=== modified file 'lib/wxkeryx/download.py'
2808--- lib/wxkeryx/download.py 2010-02-20 04:17:16 +0000
2809+++ lib/wxkeryx/download.py 2017-06-11 16:11:12 +0000
2810@@ -1,48 +1,54 @@
2811-import os.path
2812-import urllib
2813-import wx
2814+import os.path
2815+import urllib.request
2816+import wx
2817 import hashlib
2818-import wx.lib.delayedresult as delayedresult
2819-
2820+import wx.lib.delayedresult as delayedresult
2821+from gettext import gettext as _
2822+
2823 import lib
2824-from lib import consts, log
2825-
2826-class download(wx.Frame):
2827+from lib import log
2828+from lib import config
2829+
2830+
2831+class Download(wx.Frame):
2832 """This demos simplistic use of delayedresult module."""
2833 def __init__(self, parent, endfunc, files, extract=False, overwrite=False):
2834- wx.Frame.__init__(self, None, title=_("Downloading..."))
2835+ self.theme = config.theme
2836+ wx.Frame.__init__(self, parent, title=_('Downloading...'))
2837 self.parent = parent
2838 self.files = files
2839 self.function = endfunc
2840- self.extract = extract
2841- self.overwrite = overwrite
2842+ self.extract = extract
2843+ self.overwrite = overwrite
2844 self.retries = 1
2845
2846- #TODO: Add overwrite function
2847+ # TODO: Add overwrite function
2848
2849- self.SetIcon(wx.Icon(consts.fileIco, wx.BITMAP_TYPE_ICO))
2850+ self.SetIcon(wx.Icon(config.ico_path, wx.BITMAP_TYPE_ICO))
2851 panel = wx.Panel(self)
2852- loading = wx.StaticText(panel, -1, _("This may take a while. Please be patient."))
2853+ loading = wx.StaticText(panel, -1, _('This may take a while. '
2854+ 'Please be patient.'))
2855 self.gauge = wx.Gauge(panel)
2856- #cancelBtn = wx.Button(panel, -1, "Cancel")
2857
2858- self.cur = wx.StaticText(panel, -1, _("Current Transfer:"))
2859- self.cur.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
2860- self.current = wx.StaticText(panel, -1, "\n")
2861+ self.cur = wx.StaticText(panel, -1, _('Current Transfer:'))
2862+ self.cur.SetFont(wx.Font(8,
2863+ wx.FONTFAMILY_DEFAULT,
2864+ wx.FONTSTYLE_NORMAL,
2865+ wx.FONTWEIGHT_BOLD))
2866+ self.current = wx.StaticText(panel, -1, '\n')
2867 self.download_gauge = wx.Gauge(panel)
2868- self.status = wx.TextCtrl(panel, style=wx.TE_MULTILINE|wx.TE_READONLY)
2869+ self.status = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY)
2870
2871 status = wx.BoxSizer()
2872- status.Add(self.cur, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
2873- status.Add(self.current, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
2874+ status.Add(self.cur, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
2875+ status.Add(self.current, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
2876
2877 main = wx.BoxSizer(wx.VERTICAL)
2878- main.Add(loading, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5)
2879- main.Add(self.gauge, 0, wx.EXPAND|wx.ALL, 5)
2880+ main.Add(loading, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 5)
2881+ main.Add(self.gauge, 0, wx.EXPAND | wx.ALL, 5)
2882 main.Add(status)
2883- main.Add(self.download_gauge, 0, wx.EXPAND|wx.ALL, 5)
2884- main.Add(self.status, 1, wx.EXPAND|wx.ALL, 5)
2885- #main.Add(cancelBtn, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
2886+ main.Add(self.download_gauge, 0, wx.EXPAND | wx.ALL, 5)
2887+ main.Add(self.status, 1, wx.EXPAND | wx.ALL, 5)
2888
2889 panel.SetSizer(main)
2890 self.Center()
2891@@ -50,218 +56,217 @@
2892
2893 self.jobID = 0
2894 self.abortEvent = delayedresult.AbortEvent()
2895- self.Bind(wx.EVT_CLOSE, self.OnClose)
2896- #self.Bind(wx.EVT_BUTTON, self.handleAbort, cancelBtn)
2897- self.Bind(wx.EVT_TIMER, self.TimerHandler)
2898+ self.Bind(wx.EVT_CLOSE, self.on_close)
2899+ self.Bind(wx.EVT_TIMER, self.timer_handler)
2900
2901 self.timer = wx.Timer(self)
2902 self.timer.Start(100)
2903
2904 self.parent.Enable(False)
2905- self.handleGet(None)
2906-
2907- def TimerHandler(self, event): self.gauge.Pulse()
2908-
2909- def OnClose(self, event):
2910+ self.handle_get(None)
2911+
2912+ def timer_handler(self, event):
2913+ self.gauge.Pulse()
2914+
2915+ def on_close(self, event):
2916 """Only needed because in demo, closing the window does not kill the
2917 app, so worker thread continues and sends result to dead frame; normally
2918 your app would exit so this would not happen."""
2919 #if self.buttonAbort.IsEnabled():
2920 #self.log( "Exiting: Aborting job %s" % self.jobID )
2921- #self.handleAbort(None)
2922+ #self.handle_abort(None)
2923 self.Show()
2924
2925- def handleGet(self, event):
2926+ def handle_get(self, event):
2927 """Compute result in separate thread, doesn't affect GUI response."""
2928- #self.buttonGet.Enable(False)
2929- #self.buttonAbort.Enable(True)
2930 self.abortEvent.clear()
2931 self.jobID += 1
2932
2933- log.info( "Starting job %s in producer thread: GUI remains responsive"
2934- % self.jobID )
2935- delayedresult.startWorker(self._resultConsumer, self._resultProducer,
2936- wargs=(self.jobID,self.abortEvent), jobID=self.jobID)
2937-
2938-
2939- def _resultProducer(self, jobID, abortEvent):
2940+ log.info("Starting job %s in producer thread: GUI remains responsive" %
2941+ self.jobID)
2942+ delayedresult.startWorker(self._result_consumer,
2943+ self._result_producer,
2944+ wargs=(self.jobID, self.abortEvent),
2945+ jobID=self.jobID)
2946+
2947+ def _result_producer(self, jobID, abort_event):
2948 """Downloads the files in self.files"""
2949 self.numfiles = len(self.files)
2950 msg = _("Downloading ") + str(self.numfiles) + " " + _("file(s)") + "\n"
2951- wx.CallAfter(self.LogMessage, msg)
2952+ wx.CallAfter(self.log_message, msg)
2953
2954- if consts.proxy_enabled:
2955- if consts.http_proxy['http'][0:7] != 'http://':
2956- proxy = {'http://':consts.http_proxy['http']}
2957- else:
2958- proxy = consts.http_proxy
2959- downloader = Downloader(proxy)
2960- else:
2961- downloader = Downloader()
2962+ downloader = Downloader()
2963
2964 failed = self.files[:]
2965 self.numfile = 0
2966 for data in self.files:
2967- if abortEvent(): return [1, failed]
2968+ if abort_event():
2969+ return [1, failed]
2970 url = data[0]
2971- filepath = data[1]
2972- end = url.split('/')
2973- protocol = end[0] + '//'
2974- site = end[2]
2975- end = end[len(end) - 1]
2976- if len(data) <= 2:
2977- checksum = {}
2978- else:
2979+ filepath = data[1]
2980+ end = url.split('/')[-1]
2981+ if len(data) <= 2:
2982+ checksum = {}
2983+ else:
2984 checksum = data[2]
2985
2986- msg = _("Starting") + " " + end
2987- wx.CallAfter(self.SetFile, msg)
2988- #TODO: Change this to file progress (move to self.progress)
2989+ msg = _('Starting') + ' ' + end
2990+ wx.CallAfter(self.set_file, msg)
2991+ # TODO: Change this to file progress (move to self.progress)
2992
2993 self.curfile = end
2994 self.numfile += 1
2995- if os.path.exists(filepath):
2996- if not self.overwrite:
2997- if self._verify(filepath, checksum) >= 1:
2998- msg = _("Skipped: ") + end + "\n" + _("Reason: ") + _("File already exists.") + "\n"
2999- wx.CallAfter(self.LogMessage, msg)
3000- failed.remove(data)
3001- continue
3002- else:
3003- msg = _("Deleted: ") + filepath + "\n" + _("Reason: ") + _("Existing file failed checksum verify.") + "\n"
3004- wx.CallAfter(self.LogMessage, msg)
3005- os.remove(filepath)
3006- retries = 0
3007- success = False
3008- while retries <= self.retries:
3009-
3010- try: # Attempt to download the file
3011- msg = _("Downloading: ") + url + "\n"
3012- if retries != 0: msg = _("Retrying: ")+"[%i/%i] " % (retries, self.retries) + url + "\n"
3013- wx.CallAfter(self.LogMessage, msg)
3014+ if os.path.exists(filepath):
3015+ if not self.overwrite and self._verify(filepath, checksum) >= 1:
3016+ msg = _("Skipped: ") + end + "\n" + _("Reason: ") + \
3017+ _("File already exists.") + "\n"
3018+ wx.CallAfter(self.log_message, msg)
3019+ failed.remove(data)
3020+ continue
3021+ else:
3022+ msg = _("Deleted: ") + filepath + "\n" + \
3023+ _("Reason: ") + \
3024+ _("Existing file failed checksum verify.") + "\n"
3025+ wx.CallAfter(self.log_message, msg)
3026+ os.remove(filepath)
3027+ retries = 0
3028+ success = False
3029+ while retries <= self.retries:
3030+ try: # Attempt to download the file
3031+ msg = _("Downloading: ") + url + "\n"
3032+ if retries != 0:
3033+ msg = _("Retrying: ") + "[%i/%i] " % (retries, self.retries) + url + "\n"
3034+ wx.CallAfter(self.log_message, msg)
3035
3036 downloader.retrieve(url, filepath, self.progress)
3037
3038- if self._verify(filepath, checksum) == 0:
3039+ if self._verify(filepath, checksum) == 0:
3040 msg = _("Failed verify: ") + filepath + "\n"
3041- wx.CallAfter(self.LogMessage, msg)
3042+ wx.CallAfter(self.log_message, msg)
3043 os.remove(filepath)
3044- retries += 1
3045+ retries += 1
3046 continue
3047
3048- success = True
3049- failed.remove(data)
3050+ success = True
3051+ failed.remove(data)
3052 msg = _("Success: ") + filepath + "\n"
3053- wx.CallAfter(self.LogMessage, msg)
3054-
3055+ wx.CallAfter(self.log_message, msg)
3056+
3057 if self.extract:
3058 msg = _("Extracting") + " " + end
3059- wx.CallAfter(self.SetFile, msg)
3060+ wx.CallAfter(self.set_file, msg)
3061 try:
3062 import gzip
3063 infile = gzip.open(filepath, 'rb')
3064- outfile = open(filepath[:-3], 'wb')
3065+ outfile = open(filepath.strip('.gz'), 'wb')
3066 outfile.write(infile.read())
3067 outfile.close()
3068 infile.close()
3069 os.remove(filepath)
3070 except: # Failed to extract
3071 msg = _("Unable to extract: ") + filepath
3072- wx.CallAfter(self.LogMessage, msg)
3073- break
3074-
3075- except IOError, e: # Failed downloading
3076- msg = _("Failed: ") + url + "\n" + _("Reason: ") + str(e) + "\n"
3077- wx.CallAfter(self.LogMessage, msg)
3078- retries += 1
3079-
3080-
3081- msg = _("Downloaded: ") + str(len(self.files) - len(failed)) + ", " + _("Failed: ") + str(len(failed)) + "\n"
3082- wx.CallAfter(self.LogMessage, msg)
3083- if failed == []:
3084- wx.CallAfter(self.DisplayMessage,_("All downloads have been completed successfully."), _("Download Complete"))
3085- result = [0,failed]
3086- else:
3087- wx.CallAfter(self.DisplayMessage, _("Some downloads failed to complete.") + "\n" +_("Please check") + " " + os.path.join(consts.dirLog, "log") + " " + _("for more details."), _("Download Failed"))
3088- result = [1,failed]
3089- return result
3090-
3091- def _verify(self, filename, checksum, cspriority=['SHA256','SHA1','MD5sum']):
3092- """ Does a hash check on 'filename' as per hashes in 'checksum' dict. Currently only supports sha256, sha1, md5."""
3093- if cspriority != []:
3094- checks = [x for x in cspriority if checksum.has_key(x)]
3095- if checks == []: return 2
3096- else:
3097- checks = checksum.keys()
3098- if checks == []: return 2
3099-
3100- if checks[0] == 'SHA256':
3101- check = hashlib.sha256()
3102- elif checks[0] == 'SHA1':
3103- check = hashlib.sha1()
3104- elif checks[0] == 'MD5sum':
3105- check = hashlib.md5()
3106- else:
3107- return 2
3108-
3109- try:
3110- fd = open(filename, 'rb')
3111+ wx.CallAfter(self.log_message, msg)
3112+ break
3113+
3114+ except IOError as e: # Failed downloading
3115+ msg = _("Failed: ") + url + "\n" + \
3116+ _("Reason: ") + str(e) + "\n"
3117+ wx.CallAfter(self.log_message, msg)
3118+ retries += 1
3119+
3120+ msg = _("Downloaded: ") + str(len(self.files) - len(failed)) + ", " + \
3121+ _("Failed: ") + str(len(failed)) + "\n"
3122+ wx.CallAfter(self.log_message, msg)
3123+ """if not failed:
3124+ wx.CallAfter(wx.MessageBox,
3125+ _('All downloads have been completed successfully.'),
3126+ _('Download Complete'))
3127+ result = [0, []]
3128+ else:
3129+ wx.CallAfter(wx.MessageBox,
3130+ _("Some downloads failed to complete.") +
3131+ "\n" + _("Please check") + " "
3132+ + config.log_path + " "
3133+ + _("for more details."), _("Download Failed"))
3134+ result = [1, failed]"""
3135+ return [0, []]#result
3136+
3137+ def _verify(self,
3138+ filename,
3139+ checksum,
3140+ cspriority=('SHA256', 'SHA1', 'MD5sum')):
3141+ """Does a hash check on 'filename' as per hashes in 'checksum' dict.
3142+ Currently only supports sha256, sha1, md5.
3143+ """
3144+ if cspriority:
3145+ checks = [x for x in cspriority if x in checksum]
3146+ if not checks:
3147+ return 2
3148+ else:
3149+ checks = checksum.keys()
3150+ if checks:
3151+ return 2
3152+
3153+ if checks[0] == 'SHA256':
3154+ check = hashlib.sha256()
3155+ elif checks[0] == 'SHA1':
3156+ check = hashlib.sha1()
3157+ elif checks[0] == 'MD5sum':
3158+ check = hashlib.md5()
3159+ else:
3160+ return 2
3161+
3162+ with open(filename, 'rb') as fd:
3163 data = fd.read(1024*64)
3164 while data:
3165 check.update(data)
3166- data=fd.read(1024*64)
3167- fd.close()
3168- if check.hexdigest() == checksum[checks[0]]:
3169- return 1
3170- return 0
3171- except:
3172- return 2
3173+ data = fd.read(1024*64)
3174+ if check.hexdigest() == checksum[checks[0]]:
3175+ return 1
3176+ return 0
3177
3178 def progress(self, blocks, size, total):
3179- if blocks*size > total: fraction = float(total)/float(total)
3180- else: fraction = float(blocks*size)/float(total)
3181-
3182- wx.CallAfter(self.SetGauge, int(round(fraction*100,2)))
3183- msg = self.curfile + "\n" + str(int(round(fraction*100,2))) + " % of " + lib.convert_file_size(total) + " - File "+ str(self.numfile) + "/" + str(self.numfiles)
3184- wx.CallAfter(self.SetFile, msg)
3185-
3186- def LogMessage(self, msg):
3187+ if blocks*size > total:
3188+ fraction = float(total)/float(total)
3189+ else:
3190+ fraction = float(blocks*size)/float(total)
3191+
3192+ wx.CallAfter(self.set_gauge, int(round(fraction * 100, 2)))
3193+ msg = self.curfile + "\n" + str(int(round(fraction*100, 2))) + \
3194+ " % of " + lib.convert_file_size(total) + " - File " + \
3195+ str(self.numfile) + "/" + str(self.numfiles)
3196+ wx.CallAfter(self.set_file, msg)
3197+
3198+ def log_message(self, msg):
3199 log.info(msg[:-1])
3200 self.status.AppendText(msg)
3201- def DisplayMessage(self, msg, caption):
3202- wx.MessageBox(msg, caption)
3203- def SetGauge(self, val):
3204+
3205+ def set_gauge(self, val):
3206 self.download_gauge.SetValue(val)
3207- def SetFile(self, val):
3208+
3209+ def set_file(self, val):
3210 self.current.SetLabel(val)
3211
3212- def handleAbort(self, event):
3213+ def handle_abort(self, event):
3214 """Abort the result computation."""
3215- log.info( "Aborting result for job %s" % self.jobID )
3216+ log.info("Aborting result for job %s" % self.jobID)
3217 #self.buttonGet.Enable(True)
3218 #self.buttonAbort.Enable(False)
3219 self.abortEvent.set()
3220
3221-
3222- def _resultConsumer(self, delayedResult):
3223+ def _result_consumer(self, delayedResult):
3224 jobID = delayedResult.getJobID()
3225 assert jobID == self.jobID
3226- try:
3227- result = delayedResult.get()
3228- except Exception, exc:
3229- log.info( "Result for job %s raised exception: %s" % (jobID, exc) )
3230- return
3231+ result = delayedResult.get()
3232
3233 # output result
3234 #log.info( "Got result for job %s: %s" % (jobID, result) )
3235 self.parent.Enable()
3236- self.Destroy()
3237- if self.function: self.function()
3238-
3239-class Downloader(urllib.FancyURLopener):
3240- def __init__(self, proxy={}):
3241- urllib.FancyURLopener.__init__(self, proxy)
3242-
3243- def prompt_user_passwd(self, host='', realm=''):
3244- return (consts.proxy_username, consts.proxy_password)
3245-
3246+ self.DestroyLater()
3247+ if self.function:
3248+ self.function()
3249+
3250+
3251+class Downloader(urllib.request.FancyURLopener):
3252+ def __init__(self):
3253+ urllib.request.FancyURLopener.__init__(self, {})
3254
3255=== modified file 'lib/wxkeryx/editor.py'
3256--- lib/wxkeryx/editor.py 2010-02-20 04:17:16 +0000
3257+++ lib/wxkeryx/editor.py 2017-06-11 16:11:12 +0000
3258@@ -1,25 +1,26 @@
3259-import platform
3260+import platform
3261 import wx
3262-from lib import consts, project
3263+from lib import config
3264+
3265
3266 class FileEditor(wx.Dialog):
3267 def __init__(self, window_name, filename):
3268- wx.Dialog.__init__(self, None, -1, window_name)#, size=(300, 300))
3269- self.SetIcon(wx.Icon(consts.fileIco, wx.BITMAP_TYPE_ICO))
3270+ wx.Dialog.__init__(self, None, -1, window_name)
3271+ self.SetIcon(wx.Icon(config.ico_path, wx.BITMAP_TYPE_ICO))
3272 sizer = wx.BoxSizer(wx.VERTICAL)
3273 self.txt = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_RICH2)
3274 self.txt.LoadFile(filename)
3275 sizer.Add(self.txt, 1, wx.EXPAND|wx.ALL, 3)
3276
3277 # Add buttons
3278- btnsizer = wx.StdDialogButtonSizer()
3279- btnSave = wx.Button(self, wx.ID_OK, 'Save')
3280- btnSave.SetDefault()
3281- btnsizer.AddButton(btnSave)
3282- btnCancel = wx.Button(self, wx.ID_CANCEL)
3283- btnsizer.AddButton(btnCancel)
3284- btnsizer.Realize()
3285- sizer.Add(btnsizer, 0, wx.ALIGN_BOTTOM|wx.ALIGN_RIGHT|wx.ALL, 5)
3286+ btn_sizer = wx.StdDialogButtonSizer()
3287+ btn_save = wx.Button(self, wx.ID_OK, 'Save')
3288+ btn_save.SetDefault()
3289+ btn_sizer.AddButton(btn_save)
3290+ btn_cancel = wx.Button(self, wx.ID_CANCEL)
3291+ btn_sizer.AddButton(btn_cancel)
3292+ btn_sizer.Realize()
3293+ sizer.Add(btn_sizer, 0, wx.ALIGN_BOTTOM|wx.ALIGN_RIGHT|wx.ALL, 5)
3294
3295 self.SetSizer(sizer)
3296
3297@@ -30,7 +31,8 @@
3298 def comment(self, event):
3299 location = 0
3300 for line in self.txt.GetValue().split('\n'):
3301- if line.find('#') != -1: # line contains a comment
3302- self.txt.SetStyle(location + line.index('#'), len(line)+location, wx.TextAttr("RED"))
3303+ if line.find('#') != -1: # line contains a comment
3304+ self.txt.SetStyle(location + line.index('#'),
3305+ len(line) + location,
3306+ wx.TextAttr("RED"))
3307 location += len(line) + 1
3308-
3309
3310=== modified file 'lib/wxkeryx/main.py'
3311--- lib/wxkeryx/main.py 2010-03-13 19:04:52 +0000
3312+++ lib/wxkeryx/main.py 2017-06-11 16:11:12 +0000
3313@@ -18,44 +18,63 @@
3314
3315 import os.path
3316 import sys
3317+import webbrowser
3318+
3319+import time
3320 import wx
3321+import wx.adv
3322 import wx.lib.buttons as buttons
3323-import wx.lib.filebrowsebutton as filebrowse
3324 from wx.lib.mixins.listctrl import CheckListCtrlMixin
3325
3326 import lib
3327-from lib import consts, log, plugins, project
3328-from startDialog import startDialog
3329-from misc import detailsTab, ProportionalSplitter, VirtualList
3330-from options import optionDialog
3331-from delayedresult import thread
3332-from download import download
3333-from editor import FileEditor
3334+from lib import log, project
3335+from .startDialog import StartDialog
3336+from .misc import DetailsTab, ProportionalSplitter, VirtualList
3337+from .delayedresult import Thread
3338+from .download import Download
3339+from .editor import FileEditor
3340+
3341+from gettext import gettext as _
3342+
3343+from lib import config
3344+
3345
3346 class MainApp(wx.Frame):
3347 def __init__(self, *args, **kwds):
3348+ self.theme = config.theme
3349 kwds["style"] = wx.DEFAULT_FRAME_STYLE
3350 wx.Frame.__init__(self, *args, **kwds)
3351- self.splitter = ProportionalSplitter(self)#, style=wx.SP_3D|wx.SP_BORDER)
3352+ self.splitter = ProportionalSplitter(self)
3353 self.panel = wx.Panel(self.splitter)
3354 self.notebook = wx.Notebook(self.panel, -1, style=wx.NB_BOTTOM)
3355- self.project_notebook_pane = wx.Panel(self.notebook, -1, style=wx.TAB_TRAVERSAL)
3356- self.SetIcon(wx.Icon(consts.fileIco, wx.BITMAP_TYPE_ICO))
3357+ self.project_notebook_pane = wx.Panel(self.notebook,
3358+ -1,
3359+ style=wx.TAB_TRAVERSAL)
3360+ self.SetIcon(wx.Icon(config.ico_path, wx.BITMAP_TYPE_ICO))
3361 self.statusbar = self.CreateStatusBar(1, 0)
3362
3363 # Side Buttons
3364 self.buttonPanel = wx.Panel(self)
3365- self.downloadButton = buttons.GenBitmapTextButton(self.buttonPanel, -1, wx.Bitmap(consts.icon_download), _("Download"))#, size=(95, 25))
3366- self.updatesButton = buttons.GenBitmapTextButton(self.buttonPanel, -1, wx.Bitmap(consts.icon_updates), _("Get Updates"))#, size=(95, 25))
3367- self.refreshButton = buttons.GenBitmapTextButton(self.buttonPanel, -1, wx.Bitmap(consts.icon_refresh), _("Refresh"))#, size=(95, 25))
3368- #self.updateStatusButton = buttons.GenBitmapTextButton(self.buttonPanel, -1, wx.Bitmap(consts.icon_refresh), _("Update Status"))#, size=(95, 25))
3369+ self.downloadButton = buttons.GenBitmapTextButton(self.buttonPanel,
3370+ -1,
3371+ wx.Bitmap(self.theme.icon_download),
3372+ _("Download"))
3373+ self.updatesButton = buttons.GenBitmapTextButton(self.buttonPanel,
3374+ -1,
3375+ wx.Bitmap(self.theme.icon_updates),
3376+ _("Get Updates"))
3377+ self.refreshButton = buttons.GenBitmapTextButton(self.buttonPanel,
3378+ -1,
3379+ wx.Bitmap(self.theme.icon_refresh),
3380+ _("Refresh"))
3381
3382 self.notebook_il = wx.ImageList(16, 16)
3383- self.notebook_il.Add(wx.Bitmap(consts.icon_project_details))
3384+ self.notebook_il.Add(wx.Bitmap(self.theme.icon_project_details))
3385 self.notebook.SetImageList(self.notebook_il)
3386
3387- self.projectDetails = wx.TextCtrl(self.project_notebook_pane, style = wx.TE_MULTILINE | wx.TE_RICH |wx.TE_READONLY)
3388- self.details = detailsTab(self.notebook)
3389+ self.projectDetails = wx.TextCtrl(self.project_notebook_pane,
3390+ style=wx.TE_MULTILINE | wx.TE_RICH | wx.TE_READONLY)
3391+ self.details = DetailsTab(self.notebook)
3392
3393 # Create list view
3394 self.list = VirtualList(self.splitter, self.details)
3395@@ -67,123 +86,163 @@
3396 self.__do_layout()
3397 self.__bind_events()
3398
3399- plugins.load(consts.dirPlugins, self)
3400- for name, instance, type, ver in plugins.InterfacePluginList: instance.start()
3401-
3402- if len(plugins.OSPluginList) < 1:
3403- wx.MessageBox(_("No OS plugins were loaded. Make sure you have OS plugins in ") +
3404- consts.dirPlugins + ". " + _("Check the log for more details."))
3405- sys.exit(1)
3406-
3407 def __popup_menu(self):
3408 self.popupmenu = wx.Menu()
3409- #FIXME: popup menu items cannot display bitmap properly?
3410+ # FIXME: popup menu items cannot display bitmap properly?
3411 item = self.popupmenu.Append(-1, _("Download"))
3412 self.Bind(wx.EVT_MENU, self.OnDownload, item)
3413- item = self.popupmenu.Append(-1, _("View Details"))
3414- self.Bind(wx.EVT_MENU, self.OnDetails, item)
3415- self.list.Bind(wx.EVT_CONTEXT_MENU, self.OnShowPopup)
3416+ self.list.Bind(wx.EVT_CONTEXT_MENU, self.on_show_popup)
3417
3418 def __create_menu(self):
3419 # Menu Bar
3420 self.menubar = wx.MenuBar()
3421 self.fileFile = wx.Menu()
3422- self.fileClose = wx.MenuItem(self.fileFile, wx.NewId(), _("&Close Project...\tCtrl+X"), _("Closes the current project"), wx.ITEM_NORMAL)
3423- self.fileClose.SetBitmap(wx.Bitmap(consts.icon_close))
3424- self.fileFile.AppendItem(self.fileClose)
3425+ self.fileClose = wx.MenuItem(self.fileFile,
3426+ wx.NewId(),
3427+ _("&Close Project...\tCtrl+X"),
3428+ _("Closes the current project"),
3429+ wx.ITEM_NORMAL)
3430+ self.fileClose.SetBitmap(wx.Bitmap(self.theme.icon_close))
3431+ self.fileFile.Append(self.fileClose)
3432 self.fileFile.AppendSeparator()
3433- self.fileQuit = wx.MenuItem(self.fileFile, wx.NewId(), _("&Quit\tCtrl+Q"), _("Quit this application"), wx.ITEM_NORMAL)
3434- self.fileQuit.SetBitmap(wx.Bitmap(consts.icon_quit))
3435- self.fileFile.AppendItem(self.fileQuit)
3436+ self.fileQuit = wx.MenuItem(self.fileFile,
3437+ wx.NewId(),
3438+ _("&Quit\tCtrl+Q"),
3439+ _("Quit this application"),
3440+ wx.ITEM_NORMAL)
3441+ self.fileQuit.SetBitmap(wx.Bitmap(self.theme.icon_quit))
3442+ self.fileFile.Append(self.fileQuit)
3443 self.menubar.Append(self.fileFile, _("&File"))
3444
3445 tmp_menu = wx.Menu()
3446- self.editOptions = wx.MenuItem(tmp_menu, wx.NewId(), _("&Options...\tCtrl+O"), _("Open options dialog"), wx.ITEM_NORMAL)
3447- self.editOptions.SetBitmap(wx.Bitmap(consts.icon_options))
3448- tmp_menu.AppendItem(self.editOptions)
3449- self.menubar.Append(tmp_menu, _("&Edit"))
3450-
3451- tmp_menu = wx.Menu()
3452- self.projRefresh = wx.MenuItem(tmp_menu, wx.NewId(), _("&Refresh\tCtrl+R"), _("Refresh package list"), wx.ITEM_NORMAL)
3453- self.projRefresh.SetBitmap(wx.Bitmap(consts.icon_refresh))
3454- tmp_menu.AppendItem(self.projRefresh)
3455- tmp_menu.AppendSeparator()
3456- self.projUpdates = wx.MenuItem(tmp_menu, wx.NewId(), _("Get &Updates\tCtrl+U"), _("Grabs all packages that have updates"), wx.ITEM_NORMAL)
3457- self.projUpdates.SetBitmap(wx.Bitmap(consts.icon_updates))
3458- tmp_menu.AppendItem(self.projUpdates)
3459- self.projDownload = wx.MenuItem(tmp_menu, wx.NewId(), _("&Download Package\tCtrl+D"), _("Downloads selected package"), wx.ITEM_NORMAL)
3460- self.projDownload.SetBitmap(wx.Bitmap(consts.icon_download))
3461- tmp_menu.AppendItem(self.projDownload)
3462- self.projInstall = wx.MenuItem(tmp_menu, wx.NewId(), _("&Install Packages..."), _("Helps you install packages onto Project Machine"), wx.ITEM_NORMAL)
3463- self.projInstall.SetBitmap(wx.Bitmap(consts.icon_install))
3464- tmp_menu.AppendItem(self.projInstall)
3465- self.projUpdateStatus = wx.MenuItem(tmp_menu, wx.NewId(), _("&Update Status"), _("Regrabs the list of installed packages"), wx.ITEM_NORMAL)
3466- self.projUpdateStatus.SetBitmap(wx.Bitmap(consts.icon_refresh))
3467- tmp_menu.AppendItem(self.projUpdateStatus)
3468- tmp_menu.AppendSeparator()
3469- self.projDetails = wx.MenuItem(tmp_menu, wx.NewId(), _("&View Details\tCtrl+V"), _("Shows details of selected package"), wx.ITEM_NORMAL)
3470- self.projDetails.SetBitmap(wx.Bitmap(consts.icon_package))
3471- tmp_menu.AppendItem(self.projDetails)
3472- tmp_menu.AppendSeparator()
3473- self.projSources = wx.MenuItem(tmp_menu, wx.NewId(), _("&Edit Sources...\tCtrl+E"), _("Edit project sources"), wx.ITEM_NORMAL)
3474- self.projSources.SetBitmap(wx.Bitmap(consts.icon_sources))
3475- tmp_menu.AppendItem(self.projSources)
3476+ self.projRefresh = wx.MenuItem(tmp_menu,
3477+ wx.NewId(),
3478+ _("&Refresh\tCtrl+R"),
3479+ _("Refresh package list"),
3480+ wx.ITEM_NORMAL)
3481+ self.projRefresh.SetBitmap(wx.Bitmap(self.theme.icon_refresh))
3482+ tmp_menu.Append(self.projRefresh)
3483+ tmp_menu.AppendSeparator()
3484+ self.projUpdates = wx.MenuItem(tmp_menu,
3485+ wx.NewId(),
3486+ _("Get &Updates\tCtrl+U"),
3487+ _("Grabs all packages that have updates"),
3488+ wx.ITEM_NORMAL)
3489+ self.projUpdates.SetBitmap(wx.Bitmap(self.theme.icon_updates))
3490+ tmp_menu.Append(self.projUpdates)
3491+ self.projDownload = wx.MenuItem(tmp_menu,
3492+ wx.NewId(),
3493+ _("&Download Package\tCtrl+D"),
3494+ _("Downloads selected package"),
3495+ wx.ITEM_NORMAL)
3496+ self.projDownload.SetBitmap(wx.Bitmap(self.theme.icon_download))
3497+ tmp_menu.Append(self.projDownload)
3498+ self.projInstall = wx.MenuItem(tmp_menu,
3499+ wx.NewId(),
3500+ _("&Install Packages..."),
3501+ _("Helps you install packages onto "
3502+ "Project Machine"),
3503+ wx.ITEM_NORMAL)
3504+ self.projInstall.SetBitmap(wx.Bitmap(self.theme.icon_install))
3505+ tmp_menu.Append(self.projInstall)
3506+ self.projUpdateStatus = wx.MenuItem(tmp_menu,
3507+ wx.NewId(),
3508+ _("&Update Status"),
3509+ _("Regrabs the list of installed "
3510+ "packages"),
3511+ wx.ITEM_NORMAL)
3512+ self.projUpdateStatus.SetBitmap(wx.Bitmap(self.theme.icon_refresh))
3513+ tmp_menu.Append(self.projUpdateStatus)
3514+ tmp_menu.AppendSeparator()
3515+ self.projSources = wx.MenuItem(tmp_menu,
3516+ wx.NewId(),
3517+ _("&Edit Sources...\tCtrl+E"),
3518+ _("Edit project sources"),
3519+ wx.ITEM_NORMAL)
3520+ self.projSources.SetBitmap(wx.Bitmap(self.theme.icon_sources))
3521+ tmp_menu.Append(self.projSources)
3522
3523 self.menubar.Append(tmp_menu, _("&Project"))
3524
3525 tmp_menu = wx.Menu()
3526- self.helpHomepage = wx.MenuItem(tmp_menu, wx.NewId(), _("&Homepage"), _("Visit the website"), wx.ITEM_NORMAL)
3527- self.helpHomepage.SetBitmap(wx.Bitmap(consts.icon_home))
3528- tmp_menu.AppendItem(self.helpHomepage)
3529- self.helpTutorial = wx.MenuItem(tmp_menu, wx.NewId(), _("&Tutorial"), _("Read the tutorial"), wx.ITEM_NORMAL)
3530- self.helpTutorial.SetBitmap(wx.Bitmap(consts.icon_book_open))
3531- tmp_menu.AppendItem(self.helpTutorial)
3532- self.helpOnline = wx.MenuItem(tmp_menu, wx.NewId(), _("&Get Help Online"), _("Visit the forums"), wx.ITEM_NORMAL)
3533- self.helpOnline.SetBitmap(wx.Bitmap(consts.icon_help))
3534- tmp_menu.AppendItem(self.helpOnline)
3535- self.helpTranslate = wx.MenuItem(tmp_menu, wx.NewId(), _("&Translate This Application"), _("Help translate to other languages"), wx.ITEM_NORMAL)
3536- self.helpTranslate.SetBitmap(wx.Bitmap(consts.icon_translate))
3537- tmp_menu.AppendItem(self.helpTranslate)
3538- self.helpReport = wx.MenuItem(tmp_menu, wx.NewId(), _("R&eport A Problem"), _("Report a bug"), wx.ITEM_NORMAL)
3539- self.helpReport.SetBitmap(wx.Bitmap(consts.icon_bug_report))
3540- tmp_menu.AppendItem(self.helpReport)
3541- tmp_menu.AppendSeparator()
3542- self.helpDonate = wx.MenuItem(tmp_menu, wx.NewId(), _("&Donate"), _("Make a donation"), wx.ITEM_NORMAL)
3543- self.helpDonate.SetBitmap(wx.Bitmap(consts.icon_donate))
3544- tmp_menu.AppendItem(self.helpDonate)
3545- tmp_menu.AppendSeparator()
3546- self.helpAbout = wx.MenuItem(tmp_menu, wx.NewId(), _("&About..."), _("About " + consts.appName), wx.ITEM_NORMAL)
3547- self.helpAbout.SetBitmap(wx.Bitmap(consts.icon_about))
3548- tmp_menu.AppendItem(self.helpAbout)
3549- self.menubar.Append(tmp_menu, _("&Help"))
3550+ self.helpHomepage = wx.MenuItem(tmp_menu,
3551+ wx.NewId(),
3552+ _("&Homepage"),
3553+ _("Visit the website"),
3554+ wx.ITEM_NORMAL)
3555+ self.helpHomepage.SetBitmap(wx.Bitmap(self.theme.icon_home))
3556+ tmp_menu.Append(self.helpHomepage)
3557+ self.helpTutorial = wx.MenuItem(tmp_menu,
3558+ wx.NewId(),
3559+ _("&Tutorial"),
3560+ _("Read the tutorial"),
3561+ wx.ITEM_NORMAL)
3562+ self.helpTutorial.SetBitmap(wx.Bitmap(self.theme.icon_book_open))
3563+ tmp_menu.Append(self.helpTutorial)
3564+ self.helpOnline = wx.MenuItem(tmp_menu,
3565+ wx.NewId(),
3566+ _("&Get Help Online"),
3567+ _("Visit the forums"),
3568+ wx.ITEM_NORMAL)
3569+ self.helpOnline.SetBitmap(wx.Bitmap(self.theme.icon_help))
3570+ tmp_menu.Append(self.helpOnline)
3571+ self.helpTranslate = wx.MenuItem(tmp_menu,
3572+ wx.NewId(),
3573+ _("&Translate This Application"),
3574+ _("Help translate to other languages"),
3575+ wx.ITEM_NORMAL)
3576+ self.helpTranslate.SetBitmap(wx.Bitmap(self.theme.icon_translate))
3577+ tmp_menu.Append(self.helpTranslate)
3578+ self.helpReport = wx.MenuItem(tmp_menu,
3579+ wx.NewId(),
3580+ _("R&eport A Problem"),
3581+ _("Report a bug"),
3582+ wx.ITEM_NORMAL)
3583+ self.helpReport.SetBitmap(wx.Bitmap(self.theme.icon_bug_report))
3584+ tmp_menu.Append(self.helpReport)
3585+ tmp_menu.AppendSeparator()
3586+ self.helpDonate = wx.MenuItem(tmp_menu,
3587+ wx.NewId(),
3588+ _("&Donate"),
3589+ _("Make a donation"),
3590+ wx.ITEM_NORMAL)
3591+ self.helpDonate.SetBitmap(wx.Bitmap(self.theme.icon_donate))
3592+ tmp_menu.Append(self.helpDonate)
3593+ tmp_menu.AppendSeparator()
3594+ self.helpAbout = wx.MenuItem(tmp_menu,
3595+ wx.NewId(),
3596+ _("&About..."),
3597+ _("About " + config.name),
3598+ wx.ITEM_NORMAL)
3599+ self.helpAbout.SetBitmap(wx.Bitmap(self.theme.icon_about))
3600+ tmp_menu.Append(self.helpAbout)
3601+ self.menubar.Append(tmp_menu,
3602+ _("&Help"))
3603 self.SetMenuBar(self.menubar)
3604 # Menu Bar end
3605
3606 def __bind_events(self):
3607- self.Bind(wx.EVT_MENU, self.OnClose, self.fileClose)
3608- self.Bind(wx.EVT_MENU, self.OnQuit, self.fileQuit)
3609- self.Bind(wx.EVT_MENU, self.OnOptions, self.editOptions)
3610- self.Bind(wx.EVT_MENU, self.OnHomepage, self.helpHomepage)
3611- self.Bind(wx.EVT_MENU, self.OnTutorial, self.helpTutorial)
3612- self.Bind(wx.EVT_MENU, self.OnHelpOnline, self.helpOnline)
3613- self.Bind(wx.EVT_MENU, self.OnTranslate, self.helpTranslate)
3614- self.Bind(wx.EVT_MENU, self.OnReport, self.helpReport)
3615- self.Bind(wx.EVT_MENU, self.OnDonate, self.helpDonate)
3616- self.Bind(wx.EVT_MENU, self.OnAbout, self.helpAbout)
3617+ self.Bind(wx.EVT_MENU, self.on_close, self.fileClose)
3618+ self.Bind(wx.EVT_MENU, self.on_quit, self.fileQuit)
3619+ self.Bind(wx.EVT_MENU, self.on_homepage, self.helpHomepage)
3620+ self.Bind(wx.EVT_MENU, self.on_tutorial, self.helpTutorial)
3621+ self.Bind(wx.EVT_MENU, self.on_help_online, self.helpOnline)
3622+ self.Bind(wx.EVT_MENU, self.on_translate, self.helpTranslate)
3623+ self.Bind(wx.EVT_MENU, self.on_report, self.helpReport)
3624+ self.Bind(wx.EVT_MENU, self.on_donate, self.helpDonate)
3625+ self.Bind(wx.EVT_MENU, self.on_about, self.helpAbout)
3626
3627 self.Bind(wx.EVT_MENU, self.OnRefresh, self.projRefresh)
3628 self.Bind(wx.EVT_MENU, self.OnUpdates, self.projUpdates)
3629 self.Bind(wx.EVT_MENU, self.OnDownload, self.projDownload)
3630 self.Bind(wx.EVT_MENU, self.OnInstall, self.projInstall)
3631 self.Bind(wx.EVT_MENU, self.OnUpdateStatus, self.projUpdateStatus)
3632- self.Bind(wx.EVT_MENU, self.OnDetails, self.projDetails)
3633 self.Bind(wx.EVT_MENU, self.OnSources, self.projSources)
3634
3635 self.Bind(wx.EVT_BUTTON, self.OnDownload, self.downloadButton)
3636 self.Bind(wx.EVT_BUTTON, self.OnRefresh, self.refreshButton)
3637- #self.Bind(wx.EVT_BUTTON, self.OnUpdateStatus, self.updateStatusButton)
3638 self.Bind(wx.EVT_BUTTON, self.OnUpdates, self.updatesButton)
3639- self.Bind(wx.EVT_CLOSE, self.Closing)
3640+ self.Bind(wx.EVT_CLOSE, self.closing)
3641
3642 def __set_properties(self):
3643 self.SetSize((700, 500))
3644@@ -204,14 +263,16 @@
3645 sizer_buttons.Add(self.downloadButton, 0, wx.ALL, 3)
3646 sizer_buttons.Add(self.updatesButton, 0, wx.ALL, 3)
3647 sizer_buttons.Add(self.refreshButton, 0, wx.ALL, 3)
3648- #sizer_buttons.Add(self.updateStatusButton, 0, wx.ALL, 3)
3649- #sizer_bottom.Add(sizer_buttons, 0, wx.ALIGN_CENTER_VERTICAL, 0)
3650 self.buttonPanel.SetSizer(sizer_buttons)
3651
3652 sizer_project = wx.BoxSizer(wx.VERTICAL)
3653 sizer_project.Add(self.projectDetails, 1, wx.EXPAND, 0)
3654 self.project_notebook_pane.SetSizer(sizer_project)
3655- self.notebook.InsertPage(0, self.project_notebook_pane, _("Project Details"), False, 0)
3656+ self.notebook.InsertPage(0,
3657+ self.project_notebook_pane,
3658+ _("Project Details"),
3659+ False,
3660+ 0)
3661 sizer_bottom.Add(self.notebook, 1, wx.EXPAND, 0)
3662 self.panel.SetSizer(sizer_bottom)
3663 self.notebook.SetSelection(0)
3664@@ -223,143 +284,122 @@
3665 self.Layout()
3666 self.Centre()
3667
3668- def OnShowPopup(self, evt):
3669+ def on_show_popup(self, evt):
3670 pos = evt.GetPosition()
3671 pos = self.ScreenToClient(pos)
3672 self.PopupMenu(self.popupmenu, pos)
3673
3674- def Closing(self, event): # Cleanup cleanup, everybody do your share
3675- #self.thread.StopThreads()
3676- #self.thread.Destroy()
3677- log.info(_("Cleaning up plugins"))
3678- for name, instance, type, version in plugins.InterfacePluginList: instance.cleanup()
3679+ def closing(self, event): # Cleanup cleanup, everybody do your share
3680 log.info(_("Shutting down"))
3681 self.Destroy()
3682
3683- def OnClose(self, event):
3684+ def on_close(self, event):
3685 self.list.DeleteAllItems()
3686 self.projectDetails.Clear()
3687- self.list.tabpage.SetPackage()
3688- start = startDialog(None, -1, '')
3689+ self.list.tabpage.set_package()
3690+ start = StartDialog()
3691 success = start.ShowModal()
3692 start.Destroy()
3693
3694 if success == wx.ID_CANCEL:
3695 self.Close()
3696 else:
3697- self.Refresh(project.projects[len(project.projects) - 1].GetData())
3698-
3699- def OnQuit(self, event): self.Close()
3700- def OnHomepage(self, event): lib.browserOpen(consts.urlHomepage)
3701- def OnTutorial(self, event): lib.browserOpen(consts.urlTutorial)
3702- def OnHelpOnline(self, event): lib.browserOpen(consts.urlHelp)
3703- def OnTranslate(self, event): lib.browserOpen(consts.urlTranslate)
3704- def OnReport(self, event): lib.browserOpen(consts.urlBug)
3705- def OnDonate(self, event): lib.browserOpen(consts.urlDonate)
3706- def OnAbout(self, event):
3707- info = wx.AboutDialogInfo()
3708-
3709- info.SetIcon(wx.Icon(consts.fileLogo, wx.BITMAP_TYPE_PNG))
3710- info.SetName(consts.appName)
3711- info.SetVersion(consts.appVersion)
3712- info.SetDescription(consts.description)
3713- info.SetCopyright(consts.copyright)
3714- info.SetWebSite(consts.urlHomepage)
3715- info.SetLicence(consts.license)
3716- info.AddDeveloper(consts.authors)
3717- info.AddDocWriter(consts.docwriters)
3718- info.AddArtist(consts.artists)
3719- info.AddTranslator(consts.translators)
3720-
3721- wx.AboutBox(info)
3722+ self.Refresh(lib.project.current.get_data())
3723+
3724+ def on_quit(self, event):
3725+ self.Close()
3726+
3727+ def on_homepage(self, event):
3728+ webbrowser.open(config.homepage_url)
3729+
3730+ def on_tutorial(self, event):
3731+ webbrowser.open(config.tutorial_url)
3732+
3733+ def on_help_online(self, event):
3734+ webbrowser.open(config.help_url)
3735+
3736+ def on_translate(self, event):
3737+ webbrowser.open(config.translate_url)
3738+
3739+ def on_report(self, event):
3740+ webbrowser.open(config.bug_url)
3741+
3742+ def on_donate(self, event):
3743+ webbrowser.open(config.donate_url)
3744+
3745+ def on_about(self, event):
3746+ info = wx.adv.AboutDialogInfo()
3747+
3748+ info.SetIcon(wx.Icon(config.logo_path, wx.BITMAP_TYPE_PNG))
3749+ info.SetName(config.name)
3750+ info.SetVersion(config.version)
3751+ info.SetDescription(config.description)
3752+ info.SetCopyright(config.copyright)
3753+ info.SetWebSite(config.homepage_url)
3754+ info.SetLicence(config.license)
3755+ info.AddDeveloper(config.authors)
3756+ info.AddDocWriter(config.docwriters)
3757+ info.AddArtist(config.artists)
3758+ info.AddTranslator(config.translators)
3759+
3760+ wx.adv.AboutBox(info)
3761
3762 def OnSources(self, event):
3763- proj = project.projects[len(project.projects) - 1]
3764- name = proj.getSources() + _(' - Sources Editor')
3765- edit = FileEditor(name, proj.getSources())
3766+ proj = project.current
3767+ name = proj.get_sources() + _(' - Sources Editor')
3768+ edit = FileEditor(name, proj.get_sources())
3769 if edit.ShowModal() == wx.ID_OK:
3770- edit.txt.SaveFile(proj.getSources())
3771- if wx.MessageBox(_("Sources have changed. Would you like to reload the package list?"), _("Save Successful"), wx.YES_NO) == 2: #wx.ID_YES:
3772+ edit.txt.SaveFile(proj.get_sources())
3773+ if wx.MessageBox(_("Sources have changed. Would you like to reload "
3774+ "the package list?"),
3775+ _("Save Successful"),
3776+ wx.YES_NO) == 2: #wx.ID_YES:
3777 self.OnRefresh(None)
3778 edit.Destroy()
3779
3780- def OnOptions(self, event):
3781- options = optionDialog(None, -1, '')
3782- if options.ShowModal() == wx.ID_OK:
3783- # Set settings
3784- consts.proxy_enabled = options.proxyCheckBox.GetValue()
3785- conf = open(consts.file_config, 'w')
3786- conf.write('LogDir=' + options.logCtrl.GetValue() + '\n' +
3787- #'LocaleDir=' + options.localeCtrl.GetValue() + '\n' +
3788- #'PackagesDir=' + options.packagesCtrl.GetValue() + '\n' +
3789- 'PluginsDir=' + options.pluginsCtrl.GetValue() + '\n' +
3790- 'ProjectsDir=' + options.projectsCtrl.GetValue() + '\n' +
3791- 'PixmapsDir=' + options.pixmapsCtrl.GetValue() + '\n' +
3792- 'ThemesDir=' + options.themesCtrl.GetValue() + '\n' +
3793- 'DefaultTheme=' + options.defaultThemeCtrl.GetValue() + '\n' +
3794- 'CurrentTheme=' + options.defaultThemeCtrl.GetValue() + '\n')
3795-
3796- if options.proxyCheckBox.GetValue():
3797- proxy_url = 'http://%s:%s' % (options.proxy_url.GetValue(), options.proxy_port.GetValue())
3798- consts.http_proxy = {'http': proxy_url}
3799- conf.write('HTTPProxy=' + proxy_url + '\n' +
3800- 'ProxyUsername=' + options.proxy_username.GetValue() + '\n' +
3801- 'ProxyPassword=' + options.proxy_password.GetValue() + '\n')
3802- conf.close()
3803- wx.MessageBox(_("If you changed a folder location, changes take affect after restarting Keryx."), _("Saved Changes"))
3804- options.Destroy()
3805-
3806 def OnDownload(self, event):
3807 # Downloads selected package and dependencies
3808- try:
3809- selected = self.list.GetSelectedItem()
3810- if selected: files = project.projects[len(project.projects) - 1].getDependencies(selected[1])
3811- except:
3812- return
3813-
3814- # Generate filename list.
3815- list = ""
3816- for i in range(len(files)):
3817- list += files[i][0] + "\n"
3818+ selected = self.list.get_selected_item()
3819+ if selected:
3820+ files = project.current.get_dependencies(selected.name)
3821+
3822+ # Generate filename list.
3823+ list = ""
3824+ for f in files:
3825+ list += f[0] + "\n"
3826
3827 # Confirm downloads
3828- dlg = ScrolledMessageDialog(None, _("Confirm Download"), _("Do you wish to download the following") + " " + str(len(files)) + " " + _("files?"), list)
3829- #dlg = wx.MessageDialog(None, _("Do you wish to download the following") + " " + str(len(files)) + " " + _("files?") + list, _("Confirm Downloads"), wx.YES_NO | wx.ICON_QUESTION)
3830+ dlg = ScrolledMessageDialog(None,
3831+ _("Confirm Download"),
3832+ _("Do you wish to download the following")
3833+ + " " + str(len(files)) + " " + _("files?"),
3834+ list)
3835 result = dlg.ShowModal() == wx.ID_OK
3836 dlg.Destroy()
3837
3838 # User doesn't want to download them
3839 if not result:
3840- self.Refresh(project.projects[len(project.projects) -1].GetData())
3841- #self.downloadedLists()
3842 return
3843
3844 # Make sure packages directory exists
3845- dir = os.path.join(project.projects[len(project.projects) -1].dir, 'packages')
3846+ dir = os.path.join(project.current.dir, 'packages')
3847 if not os.path.exists(dir):
3848 os.mkdir(dir)
3849
3850- #########################################################
3851- # FIXME: Return to the plugin for post download cleanup #
3852- #########################################################
3853- frame = download(self, None, files)
3854- #self.downloads.files = files
3855- #self.thread.start(self, self.downloads.start, None) #self.downloadedLsts) # Do nothing after the packages are downloaded
3856- #a.start(self, lists.start, None) # New thread
3857-
3858- # If it fails, reset the package lists, in other words, reload the list again.
3859-
3860- #self.download.downloads = files
3861- #self.download.start()
3862-
3863- #FIXME: If any package fails to download, set package installed version to blank
3864- #list of failed files now stored in frame[1] as returned by download.py
3865-
3866+ # FIXME: Return to the plugin for post download cleanup
3867+ Download(self, None, files)
3868+
3869+ # FIXME: If any package fails to download, set package installed version
3870+ # to blank list of failed files now stored in frame[1] as returned by
3871+ # download.py
3872+
3873 def OnInstall(self, event):
3874 # Get the necessary information for package installation
3875- projdir = project.projects[len(project.projects) -1].dir
3876+ projdir = project.current.dir
3877 spacksdir = os.path.join(projdir, 'packages')
3878- spacks = [x for x in os.listdir(spacksdir) if os.path.isfile(os.path.join(spacksdir, x)) and (os.path.splitext(x)[1] == '.deb')]
3879+ spacks = [x for x in os.listdir(spacksdir)
3880+ if os.path.isfile(os.path.join(spacksdir, x)) and
3881+ (os.path.splitext(x)[1] == '.deb')]
3882 packnames = sorted([x[:x.find('_')] for x in spacks])
3883
3884 # Ask what packages need to be installed
3885@@ -373,7 +413,7 @@
3886 if result == wx.ID_CANCEL:
3887 return
3888
3889- result = project.projects[len(project.projects) - 1].plugin.installCache(project.projects[len(project.projects) -1].dir, 'installcache')
3890+ result = project.current.plugin.install_cache(project.current.dir, 'installcache')
3891 if not result:
3892 wx.MessageBox(_("Failed to update the APT cache. Aborting " \
3893 "installation."),
3894@@ -381,19 +421,18 @@
3895 return
3896
3897 # Install the packages
3898- projdir = project.projects[len(project.projects) -1].dir
3899- result = project.projects[len(project.projects) - 1]. \
3900- plugin.installPacks(projdir, ' '.join(packnames))
3901+ projdir = project.current.dir
3902+ result = project.current.plugin.install_packs(projdir, ' '.join(packnames))
3903 if result:
3904- wx.MessageBox(_("Package installation finished. You should update " \
3905+ wx.MessageBox(_("Package installation finished. You should update "
3906 "your project status (Project > Update Status)."),
3907 _("Installation finished"))
3908 else:
3909- wx.MessageBox(_("An error occurred while installing your " \
3910- "packages. Make sure no other package manager is " \
3911- "running when you try to Install the packages. " \
3912- "Check the log for more details.\n\nAnother " \
3913- "option is installing the packages by hand using " \
3914+ wx.MessageBox(_("An error occurred while installing your "
3915+ "packages. Make sure no other package manager is "
3916+ "running when you try to Install the packages. "
3917+ "Check the log for more details.\n\nAnother "
3918+ "option is installing the packages by hand using "
3919 "apt-get."),
3920 _("Install Error"))
3921
3922@@ -405,97 +444,78 @@
3923
3924 # Make sure we have updates
3925 if len(updates) == 0:
3926- return wx.MessageBox(_("No updates available. Your computer is up-to-date."),_("No updates"))
3927+ return wx.MessageBox(_("No updates available. Your computer is "
3928+ "up-to-date."),
3929+ _("No updates"))
3930
3931- # Got a list of package names, lets generate a list of filenames to download
3932+ # Got a list of package names, lets generate a list of filenames to
3933+ # download
3934 for item in updates:
3935- files += project.projects[len(project.projects) - 1].getDependencies(item)
3936+ files += project.current.get_dependencies(item)
3937
3938 # Make sure packages directory exists
3939- dir = os.path.join(project.projects[len(project.projects) - 1].dir, 'packages')
3940+ dir = os.path.join(project.current.dir, 'packages')
3941 if not os.path.exists(dir):
3942 os.mkdir(dir)
3943
3944- frame = download(self, None, files)
3945-
3946- def OnDetails(self, event):
3947- for i in range(self.notebook.GetPageCount()): # Find the details page and select it
3948- if self.notebook.GetPageText(i) == _("Package Details"): self.notebook.SetSelection(i)
3949-
3950-# def OnOpen(self, event):
3951-# dlg = wx.FileDialog(self, message=_("Choose a file"),
3952-# defaultDir=consts.dirProjects,
3953-# defaultFile="",
3954-# wildcard=consts.wildcard,
3955-# style=wx.OPEN | wx.CHANGE_DIR)
3956-
3957-# # Show the dialog and retrieve the user response. If it is the OK response,
3958-# # process the data
3959-# if dlg.ShowModal() == wx.ID_OK:
3960-# paths = dlg.GetPaths()
3961-# data = project.openKeryx(paths[0])
3962-# dlg.Destroy()
3963-# if data:
3964-# proj = project.project(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7])
3965-# project.projects[len(project.projects) -1] = proj # variable must be initialized
3966-# self.Refresh(project.projects[len(project.projects) -1].GetData())
3967-
3968-# def OnNew(self, event):
3969-# dlg = wx.TextEntryDialog(self, _("What would you like to name your new project?"),
3970-# _('New Project'), platform.node())
3971-# if dlg.ShowModal() == wx.ID_OK:
3972-# response = dlg.GetValue()
3973-# #print response
3974-# dlg.Destroy()
3975+ Download(self, None, files)
3976
3977 def OnUpdateStatus(self, event):
3978- dlg = wx.MessageDialog(None, _("This will update Keryx about which packages are installed on your computer. Only run this on the computer you created this project on.\n\n" + \
3979- "Would you like to continue?"),
3980- _("Update Status"), wx.YES_NO | wx.ICON_QUESTION)
3981+ dlg = wx.MessageDialog(None,
3982+ _("This will update Keryx about which packages "
3983+ "are installed on your computer. Only run "
3984+ "this on the computer you created this "
3985+ "project on.\n\nWould you like to continue?"),
3986+ _("Update Status"), wx.YES_NO | wx.ICON_QUESTION)
3987 result = dlg.ShowModal()
3988 dlg.Destroy()
3989 if result == wx.ID_NO:
3990 return
3991 success = self.UpdateStatus()
3992 if success:
3993- dlg = wx.MessageBox(_("Status update Succeeded."),
3994+ wx.MessageBox(_("Status update Succeeded."),
3995 _("Status Update Succeeded"))
3996 self.loadLocal()
3997 else:
3998- dlg = wx.MessageBox(_("Status update failed. Try running Keryx as root (using 'sudo')."),
3999- _("Status Update Failed"), wx.ICON_ERROR)
4000+ wx.MessageBox(_("Status update failed. Try running Keryx as root "
4001+ "(using 'sudo')."),
4002+ _("Status Update Failed"),
4003+ wx.ICON_ERROR)
4004+
4005 def UpdateStatus(self):
4006- project_dir = project.projects[len(project.projects) -1].dir
4007- return project.projects[len(project.projects) - 1].plugin.updateStatus(project_dir)
4008-
4009-
4010+ project_dir = project.current.dir
4011+ return project.current.plugin.update_status(project_dir)
4012+
4013 def OnRefresh(self, event):
4014- self.Refresh(project.projects[len(project.projects) -1].GetData())
4015+ self.Refresh(project.current.get_data())
4016+
4017 def Refresh(self, data):
4018- self.SetTitle(data[0] + " - " + consts.appName + " v" + consts.appVersion)
4019+ self.SetTitle(data[0] + " - " + config.name + " v" + config.version)
4020 self.projectDetails.Clear()
4021 self.projectDetails.AppendText(data[0] + '\n')
4022- if data[2] != '': self.projectDetails.AppendText(_("Operating System:") + '\t' + data[2] + '\n')
4023- if data[3] != '': self.projectDetails.AppendText(_("Version:") + '\t' + data[3] + '\n')
4024- if data[4] != '': self.projectDetails.AppendText(_("Kernel:") + '\t' + data[5] + '\n')
4025- if data[5] != '': self.projectDetails.AppendText(_("Architecture:") + '\t' + data[4] + '\n')
4026- self.projectDetails.AppendText(_("Drive Free Space:") + '\t' + lib.driveFreeSpace() + '\n')
4027- #self.projectDetails.AppendText("%s\n%s\t%s\n%s\t%s\n%s\t%s\n%s\t%s\n%s\t%s" %
4028- # (data[0], os, data[2], v, data[3], k, data[4], a, data[5], f, lib.driveFreeSpace()))
4029+ if data[2] != '':
4030+ self.projectDetails.AppendText(_("Operating System:") + '\t' + data[2] + '\n')
4031+ if data[3] != '':
4032+ self.projectDetails.AppendText(_("Version:") + '\t' + data[3] + '\n')
4033+ if data[4] != '':
4034+ self.projectDetails.AppendText(_("Kernel:") + '\t' + data[5] + '\n')
4035+ if data[5] != '':
4036+ self.projectDetails.AppendText(_("Architecture:") + '\t' + data[4] + '\n')
4037+ self.projectDetails.AppendText(_("Drive Free Space:") + '\t' + lib.drive_free_space() + '\n')
4038
4039 points = self.projectDetails.GetFont().GetPointSize()
4040 style = wx.Font(points, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
4041 self.projectDetails.SetStyle(0, len(data[0]), wx.TextAttr("black", wx.NullColour, style))
4042
4043 # Get index file URLs.
4044- urls = project.projects[len(project.projects) -1].getUrls()
4045+ urls = project.current.get_urls()
4046
4047 # Inquire whether latest package list should be downloaded
4048- #dlg = wx.MessageDialog(None, _('Download the latest package list?'),
4049- # _("Download latest?"), wx.YES_NO | wx.ICON_QUESTION)
4050 urls_string = '\n'.join(['\n'.join(x) for x in urls])
4051- dlg = ScrolledMessageDialog(None, _("Download Latest?"),
4052- _("Download the latest package lists?"), urls_string)
4053+ dlg = ScrolledMessageDialog(None,
4054+ _("Download Latest?"),
4055+ _("Download the latest package lists?"),
4056+ urls_string)
4057
4058 result = dlg.ShowModal()
4059 dlg.Destroy()
4060@@ -503,29 +523,28 @@
4061 # Load package list
4062 self.SetStatusText(_("Loading packages") + "...", 0)
4063
4064- if result == wx.ID_OK: frame = download(self, self.loadLocal, urls, True)
4065- else: self.loadLocal()
4066-
4067+ if result == wx.ID_OK:
4068+ Download(self, self.loadLocal, urls, True)
4069+ else:
4070+ self.loadLocal()
4071
4072 def loadLocal(self, result=None):
4073 #TODO: Verify downloads?
4074- thread(self, project.projects[len(project.projects) - 1].loadLocal, self.loadPackages)
4075+ Thread(self, project.current.load_local, self.loadPackages)
4076
4077 def loadPackages(self, result=None):
4078- proj = project.projects[len(project.projects) - 1]
4079- #self.thread.Start(project.LocalPackagesThread(proj), None)
4080- self.list.SetMap(proj.packages)
4081- self.list.SortListItems(1, 1) # Sort column 1, ascending A->Z order
4082+ proj = project.current
4083+ self.list.set_map(proj.packages)
4084+ self.list.SortListItems(1) # Sort column 1, ascending A->Z order
4085
4086- self.SetStatusText(str(len(proj.packages.keys())) + " " + _("Packages"),0)
4087+ self.SetStatusText(str(len(proj.packages.keys())) + " " + _("Packages"), 0)
4088
4089 # end of class MainApp
4090
4091-# Begin mac9416's wonderful custom dialog!
4092
4093 class ScrolledMessageDialog(wx.Dialog):
4094 def __init__(self, parent, title, caption, msg,
4095- pos=wx.DefaultPosition, size=(400,250),
4096+ pos=wx.DefaultPosition, size=(400, 250),
4097 style=wx.DEFAULT_DIALOG_STYLE):
4098 wx.Dialog.__init__(self, parent, -1, title, pos, size, style)
4099 x, y = pos
4100@@ -553,6 +572,7 @@
4101 sizer.Add(btnSizer, 0, wx.ALIGN_RIGHT | wx.TOP | wx.BOTTOM, 10)
4102 self.SetSizerAndFit(sizer)
4103
4104+
4105 class SelectPackagesDialog(wx.Dialog):
4106 """Dialog for selection of packages to be installed"""
4107 def __init__(self, parent, id, title, items):
4108@@ -602,6 +622,7 @@
4109 for index in range(self.check_list.list.GetItemCount()):
4110 self.check_list.list.CheckItem(index, False)
4111
4112+
4113 class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin):
4114 def __init__(self, parent):
4115 wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT)
4116@@ -611,6 +632,7 @@
4117 def OnItemActivated(self, evt):
4118 self.ToggleItem(evt.m_itemIndex)
4119
4120+
4121 class CheckListPanel(wx.Panel):
4122 def __init__(self, parent, items):
4123 wx.Panel.__init__(self, parent, -1)
4124
4125=== modified file 'lib/wxkeryx/misc.py'
4126--- lib/wxkeryx/misc.py 2010-02-20 04:17:16 +0000
4127+++ lib/wxkeryx/misc.py 2017-06-11 16:11:12 +0000
4128@@ -16,158 +16,176 @@
4129 # along with this program; if not, write to the Free Software
4130 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4131
4132-import os.path
4133-import urllib
4134 import wx
4135-import wx.lib.delayedresult as delayedresult
4136-import wx.lib.mixins.listctrl as listmix
4137-
4138+import wx.lib.mixins.listctrl as listmix
4139+from gettext import gettext as _
4140+
4141 import lib
4142-from lib import consts, log
4143-
4144-class VirtualList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ColumnSorterMixin):
4145- def __init__(self, parent, tabpage, data = {}):
4146- wx.ListCtrl.__init__( self, parent, -1, style=wx.LC_SINGLE_SEL|wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
4147+from lib import config
4148+from lib.package import Package
4149+
4150+
4151+class VirtualList(wx.ListCtrl,
4152+ listmix.ListCtrlAutoWidthMixin,
4153+ listmix.ColumnSorterMixin):
4154+ def __init__(self, parent, tabpage, data={}):
4155+ self.theme = config.theme
4156+ wx.ListCtrl.__init__(self,
4157+ parent,
4158+ -1,
4159+ style=wx.LC_SINGLE_SEL |
4160+ wx.LC_REPORT |
4161+ wx.LC_VIRTUAL |
4162+ wx.LC_HRULES |
4163+ wx.LC_VRULES)
4164 self.tabpage = tabpage
4165 # Add some art
4166 self.il = wx.ImageList(16, 16)
4167- self.sm_up = self.il.Add(wx.Bitmap(consts.icon_arrow_up))
4168- self.sm_dn = self.il.Add(wx.Bitmap(consts.icon_arrow_down))
4169- self.uptodate = self.il.Add(wx.Bitmap(consts.icon_uptodate))
4170- self.update = self.il.Add(wx.Bitmap(consts.icon_update))
4171- self.error = self.il.Add(wx.Bitmap(consts.icon_error))
4172- self.downloaded = self.il.Add(wx.Bitmap(consts.icon_downloaded))
4173+ self.sm_up = self.il.Add(wx.Bitmap(self.theme.icon_arrow_up))
4174+ self.sm_dn = self.il.Add(wx.Bitmap(self.theme.icon_arrow_down))
4175+ self.uptodate = self.il.Add(wx.Bitmap(self.theme.icon_uptodate))
4176+ self.update = self.il.Add(wx.Bitmap(self.theme.icon_update))
4177+ self.error = self.il.Add(wx.Bitmap(self.theme.icon_error))
4178+ self.downloaded = self.il.Add(wx.Bitmap(self.theme.icon_downloaded))
4179 self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
4180
4181 # Add some attributes (colourful background for each item rows)
4182 self.attrUpdate = wx.ListItemAttr()
4183- self.attrUpdate.SetBackgroundColour("light blue") # blue
4184+ self.attrUpdate.SetBackgroundColour("light blue") # blue
4185 self.attrUptodate = wx.ListItemAttr()
4186- self.attrUptodate.SetBackgroundColour(wx.Color(150, 235, 140)) # green
4187+ self.attrUptodate.SetBackgroundColour(wx.Colour(150, 235, 140)) # green
4188 self.attrError = wx.ListItemAttr()
4189- self.attrError.SetBackgroundColour(wx.Color(255, 118, 106)) # red
4190+ self.attrError.SetBackgroundColour(wx.Colour(255, 118, 106)) # red
4191 self.attrDownloaded = wx.ListItemAttr()
4192- self.attrDownloaded.SetBackgroundColour(wx.Color(255, 215, 100)) # yellow
4193+ self.attrDownloaded.SetBackgroundColour(wx.Colour(255, 215, 100)) # yellow
4194
4195 # Build the columns
4196 count = 0
4197- for title, width in consts.columns:
4198+
4199+ columns = [(_('S'), 50),
4200+ (_('Package Name'), 200),
4201+ (_('Installed Version'), 200),
4202+ (_('Latest Version'), 200),
4203+ (_('Description'), 200)]
4204+
4205+ for title, width in columns:
4206 self.InsertColumn(count, title)
4207 self.SetColumnWidth(count, width)
4208 count += 1
4209- #self.InsertColumn(0, _("S"))
4210- #self.InsertColumn(1, _("Package Name"))
4211- #self.InsertColumn(2, _("Installed Version"))
4212- #self.InsertColumn(3, _("Latest Version"))
4213- #self.InsertColumn(4, _("Description"))
4214-
4215- #self.SetColumnWidth(0, 50)
4216- #self.SetColumnWidth(1, 200)
4217- #self.SetColumnWidth(2, 200)
4218- #self.SetColumnWidth(3, 200)
4219- #self.SetColumnWidth(4, 200)
4220-
4221- #These two should probably be passed to init more cleanly
4222- #setting the numbers of items = number of elements in the dictionary
4223- self.SetMap(data)
4224+
4225+ # These two should probably be passed to init more cleanly setting the
4226+ # numbers of items = number of elements in the dictionary
4227+ self.set_map(data)
4228 self.currentItem = None
4229
4230- #mixins
4231+ # mixins
4232 listmix.ListCtrlAutoWidthMixin.__init__(self)
4233 listmix.ColumnSorterMixin.__init__(self, 3)
4234
4235- #sort by genre (column 2), A->Z ascending order (1)
4236- #self.SortListItems(0, 1)
4237-
4238- #events
4239- self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
4240- self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
4241- self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
4242-
4243- def SetMap(self, dataMap):
4244- self.itemDataMap = dataMap
4245- self.itemIndexMap = dataMap.keys()
4246- self.SetItemCount(len(dataMap))
4247- self.tabpage.SetPackage()
4248+ # events
4249+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_item_selected)
4250+ self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_item_selected)
4251+ self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._on_item_deselected)
4252+
4253+ def set_map(self, data_map):
4254+ self.itemDataMap = data_map
4255+ self.itemIndexMap = data_map.keys()
4256+ self.SetItemCount(len(data_map))
4257+ self.tabpage.set_package()
4258 self.Refresh()
4259
4260- def OnColClick(self,event): event.Skip()
4261-
4262- def GetSelectedItem(self): return self.itemDataMap[self.itemIndexMap[self.currentItem]]
4263-
4264- def OnItemSelected(self, event):
4265- self.currentItem = event.m_itemIndex
4266- index=self.itemIndexMap[self.currentItem]
4267- self.tabpage.SetPackage(self.itemDataMap[index])
4268-
4269- def OnItemActivated(self, event):
4270- self.currentItem = event.m_itemIndex
4271- #print 'item activated'
4272-
4273- def getColumnText(self, index, col):
4274+ def on_col_click(self, event):
4275+ event.Skip()
4276+
4277+ def get_selected_item(self):
4278+ return self.itemDataMap[self.itemIndexMap[self.currentItem]]
4279+
4280+ def on_item_selected(self, event):
4281+ self.currentItem = event.Index
4282+ index = self.itemIndexMap[self.currentItem]
4283+ self.tabpage.set_package(self.itemDataMap[index])
4284+
4285+ def _on_item_selected(self, event):
4286+ self.currentItem = event.m_itemIndex
4287+
4288+ def get_column_text(self, index, col):
4289 item = self.GetItem(index, col)
4290 return item.GetText()
4291
4292- def OnItemDeselected(self, evt):
4293+ def _on_item_deselected(self, evt):
4294 pass
4295
4296 def OnGetItemText(self, item, col):
4297- index=self.itemIndexMap[item]
4298- s = self.itemDataMap[index][col]
4299- return s
4300+ index = self.itemIndexMap[item]
4301+ if col == 0:
4302+ return self.itemDataMap[index].status
4303+ elif col == 1:
4304+ return self.itemDataMap[index].name
4305+ elif col == 2:
4306+ return self.itemDataMap[index].installed_version
4307+ elif col == 3:
4308+ return self.itemDataMap[index].version
4309+ elif col == 4:
4310+ return self.itemDataMap[index].description
4311
4312 def OnGetItemImage(self, item):
4313- index=self.itemIndexMap[item]
4314- icon=self.itemDataMap[index][0]
4315+ index = self.itemIndexMap[item]
4316+ icon = self.itemDataMap[index].status
4317
4318- if icon==0:
4319+ if icon == 0:
4320 return self.uptodate
4321- elif icon==1:
4322+ elif icon == 1:
4323 return self.update
4324- elif icon==2:
4325+ elif icon == 2:
4326 return self.error
4327- elif icon==3:
4328+ elif icon == 3:
4329 return self.downloaded
4330 else:
4331 return -1
4332
4333 def OnGetItemAttr(self, item):
4334- index=self.itemIndexMap[item]
4335- update=self.itemDataMap[index][0]
4336+ index = self.itemIndexMap[item]
4337+ update = self.itemDataMap[index].status
4338
4339- if update==0:
4340+ if update == 0:
4341 return self.attrUptodate
4342- elif update==1:
4343+ elif update == 1:
4344 return self.attrUpdate
4345- elif update==2:
4346+ elif update == 2:
4347 return self.attrError
4348- elif update==3:
4349+ elif update == 3:
4350 return self.attrDownloaded
4351 else:
4352 return None
4353
4354- def SortItems(self,sorter=cmp):
4355+ def SortItems(self, sorter):
4356 items = list(self.itemDataMap.keys())
4357- items.sort(sorter)
4358+ items.sort()
4359 self.itemIndexMap = items
4360
4361 # redraw the list
4362 self.Refresh()
4363
4364 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
4365- def GetListCtrl(self): return self
4366+ def GetListCtrl(self):
4367+ return self
4368
4369 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
4370- def GetSortImages(self): return (self.sm_dn, self.sm_up)
4371-
4372-# end of class VirtualList
4373-
4374-class detailsTab(object):
4375+ def GetSortImages(self):
4376+ return (self.sm_dn, self.sm_up)
4377+
4378+
4379+class DetailsTab(object):
4380 def __init__(self, notebook):
4381+ self.theme = config.theme
4382 self.notebook = notebook
4383 self.pane = wx.Panel(self.notebook, -1, style=wx.TAB_TRAVERSAL)
4384- index = self.notebook.GetImageList().Add(wx.Bitmap(consts.icon_package))
4385- self.details = wx.TextCtrl(self.pane, style = wx.TE_MULTILINE | wx.TE_RICH | wx.TE_READONLY)
4386+ index = self.notebook.GetImageList().\
4387+ Add(wx.Bitmap(self.theme.icon_package))
4388+ self.details = wx.TextCtrl(self.pane,
4389+ style=wx.TE_MULTILINE |
4390+ wx.TE_RICH |
4391+ wx.TE_READONLY)
4392
4393 attr = wx.TextAttr()
4394 attr.SetTabs([400])
4395@@ -178,92 +196,123 @@
4396 self.pane.SetSizer(sizer)
4397 self.notebook.AddPage(self.pane, _("Package Details"), False, index)
4398
4399- def SetPackage(self, stats = ['','','','','','','',0]):
4400+ def set_package(self, stats=Package('')):
4401 self.details.Clear()
4402- if stats[1] == '': return
4403-
4404- update = stats[0]
4405- name = stats[1]
4406- inst = stats[2]
4407- latest = stats[3]
4408- descrip = stats[4]
4409- depends = stats[5]
4410- filename = stats[6]
4411- size = lib.convert_file_size(stats[7])
4412-
4413- if not name == '': self.details.AppendText(name + '\n')
4414- if not descrip == '': self.details.AppendText(_("Description:\t") + descrip + '\n')
4415- if not inst == '': self.details.AppendText(_("Installed Version:\t") + inst + '\n')
4416- if not latest == '': self.details.AppendText(_("Latest Version:\t") + latest + '\n')
4417- if not depends =='': self.details.AppendText(_("Dependencies:\t") + depends + '\n')
4418- if not size == '': self.details.AppendText(_("Size:\t") + size + '\n')
4419- if not filename == '': self.details.AppendText(_("Filename:\t") + filename + '\n')
4420- #self.details.AppendText("%s\n%s\t%s\n%s\t%s\n%s\t%s\n%s\t%s\n%s\t%s\n%s\t%s" %
4421- # (name, d, descrip, i, inst, l, latest, dep, depends, s, size, f, filename))
4422+ if stats.name == '':
4423+ return
4424+
4425+ update = stats.status
4426+ name = stats.name
4427+ inst = stats.installed_version
4428+ latest = stats.version
4429+ descrip = stats.description
4430+ depends = stats.depends
4431+ filename = stats.filename
4432+ size = lib.convert_file_size(stats.size)
4433+
4434+ if not name == '':
4435+ self.details.AppendText(name + '\n')
4436+ if not descrip == '':
4437+ self.details.AppendText(_("Description:\t") + descrip + '\n')
4438+ if not inst == '':
4439+ self.details.AppendText(_("Installed Version:\t") + inst + '\n')
4440+ if not latest == '':
4441+ self.details.AppendText(_("Latest Version:\t") + latest + '\n')
4442+ if not depends == '':
4443+ self.details.AppendText(_("Dependencies:\t") + depends + '\n')
4444+ if not size == '':
4445+ self.details.AppendText(_("Size:\t") + size + '\n')
4446+ if not filename == '':
4447+ self.details.AppendText(_("Filename:\t") + filename + '\n')
4448
4449 # Fixes scrolling issue
4450 self.details.ScrollLines(-15)
4451
4452 points = self.details.GetFont().GetPointSize()
4453- style = wx.Font(points, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
4454- self.details.SetStyle(0, len(name), wx.TextAttr("black", wx.NullColour, style))
4455+ style = wx.Font(points,
4456+ wx.FONTFAMILY_DEFAULT,
4457+ wx.FONTSTYLE_NORMAL,
4458+ wx.FONTWEIGHT_BOLD)
4459+ self.details.SetStyle(0,
4460+ len(name),
4461+ wx.TextAttr("black", wx.NullColour, style))
4462
4463-# end of class detailsTab
4464
4465 class ProportionalSplitter(wx.SplitterWindow):
4466- def __init__(self,parent, id = -1, proportion=0.60, size = wx.DefaultSize, **kwargs):
4467- wx.SplitterWindow.__init__(self,parent,id,wx.Point(0, 0),size, **kwargs)
4468- self.SetMinimumPaneSize(50) #the minimum size of a pane.
4469- self.proportion = proportion
4470- if not 0 < self.proportion < 1:
4471- raise ValueError, "proportion value for ProportionalSplitter must be between 0 and 1."
4472- self.ResetSash()
4473- self.Bind(wx.EVT_SIZE, self.OnReSize)
4474- self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged, id=id)
4475- ##hack to set sizes on first paint event
4476- self.Bind(wx.EVT_PAINT, self.OnPaint)
4477- self.firstpaint = True
4478-
4479- def SplitHorizontally(self, win1, win2):
4480- if self.GetParent() is None: return False
4481- return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
4482- int(round(self.GetParent().GetSize().GetHeight() * self.proportion)))
4483-
4484- def SplitVertically(self, win1, win2):
4485- if self.GetParent() is None: return False
4486- return wx.SplitterWindow.SplitVertically(self, win1, win2,
4487- int(round(self.GetParent().GetSize().GetWidth() * self.proportion)))
4488-
4489- def GetExpectedSashPosition(self):
4490- if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
4491- tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
4492- else:
4493- tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
4494- return int(round(tot * self.proportion))
4495-
4496- def ResetSash(self):
4497- self.SetSashPosition(self.GetExpectedSashPosition())
4498-
4499- def OnReSize(self, event):
4500- """Window has been resized, so we need to adjust the sash based on self.proportion."""
4501- self.ResetSash()
4502- event.Skip()
4503-
4504- def OnSashChanged(self, event):
4505- """We'll change self.proportion now based on where user dragged the sash."""
4506- pos = float(self.GetSashPosition())
4507- if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
4508- tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
4509- else:
4510- tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
4511- self.proportion = pos / tot
4512- event.Skip()
4513-
4514- def OnPaint(self,event):
4515- if self.firstpaint:
4516- if self.GetSashPosition() != self.GetExpectedSashPosition():
4517- self.ResetSash()
4518- self.firstpaint = False
4519- event.Skip()
4520-
4521-# end of class ProportionalSplitter
4522+ def __init__(self,
4523+ parent,
4524+ id=-1,
4525+ proportion=0.60,
4526+ size=wx.DefaultSize,
4527+ **kwargs):
4528+ wx.SplitterWindow.__init__(self,
4529+ parent,
4530+ id,
4531+ wx.Point(0, 0),
4532+ size,
4533+ **kwargs)
4534+ self.SetMinimumPaneSize(50)
4535+ self.proportion = proportion
4536+ if not 0 < self.proportion < 1:
4537+ raise ValueError("proportion value for "
4538+ "ProportionalSplitter must be between 0 "
4539+ "and 1.")
4540+ self.ResetSash()
4541+ self.Bind(wx.EVT_SIZE, self.OnReSize)
4542+ self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED,
4543+ self.OnSashChanged,
4544+ id=id)
4545+ self.Bind(wx.EVT_PAINT, self.OnPaint)
4546+ self.firstpaint = True
4547+
4548+ def SplitHorizontally(self, win1, win2):
4549+ if self.GetParent() is None: return False
4550+ return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
4551+ int(round(self.GetParent().GetSize().GetHeight() *
4552+ self.proportion)))
4553+
4554+ def SplitVertically(self, win1, win2):
4555+ if self.GetParent() is None: return False
4556+ return wx.SplitterWindow.SplitVertically(self, win1, win2,
4557+ int(round(self.GetParent().GetSize().GetWidth() *
4558+ self.proportion)))
4559+
4560+ def GetExpectedSashPosition(self):
4561+ if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
4562+ tot = max(self.GetMinimumPaneSize(),
4563+ self.GetParent().GetClientSize().height)
4564+ else:
4565+ tot = max(self.GetMinimumPaneSize(),
4566+ self.GetParent().GetClientSize().width)
4567+ return int(round(tot * self.proportion))
4568+
4569+ def ResetSash(self):
4570+ self.SetSashPosition(self.GetExpectedSashPosition())
4571+
4572+ def OnReSize(self, event):
4573+ """Window has been resized, so we need to adjust the sash based on
4574+ self.proportion.
4575+ """
4576+ self.ResetSash()
4577+ event.Skip()
4578+
4579+ def OnSashChanged(self, event):
4580+ """We'll change self.proportion now based on where user dragged the
4581+ sash.
4582+ """
4583+ pos = float(self.GetSashPosition())
4584+ if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
4585+ tot = max(self.GetMinimumPaneSize(),
4586+ self.GetParent().GetClientSize().height)
4587+ else:
4588+ tot = max(self.GetMinimumPaneSize(),
4589+ self.GetParent().GetClientSize().width)
4590+ self.proportion = pos / tot
4591+ event.Skip()
4592+
4593+ def OnPaint(self, event):
4594+ if self.firstpaint:
4595+ if self.GetSashPosition() != self.GetExpectedSashPosition():
4596+ self.ResetSash()
4597+ self.firstpaint = False
4598+ event.Skip()
4599
4600=== removed file 'lib/wxkeryx/options.py'
4601--- lib/wxkeryx/options.py 2010-03-05 00:06:56 +0000
4602+++ lib/wxkeryx/options.py 1970-01-01 00:00:00 +0000
4603@@ -1,300 +0,0 @@
4604-# -*- coding: utf-8 -*-
4605-#
4606-# Author: Chris Oliver (excid3@gmail.com)
4607-#
4608-# This program is free software; you can redistribute it and/or modify
4609-# it under the terms of the GNU General Public License as published by
4610-# the Free Software Foundation; either version 2 of the License, or
4611-# (at your option) any later version.
4612-#
4613-# This program is distributed in the hope that it will be useful,
4614-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4615-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4616-# GNU Library General Public License for more details.
4617-#
4618-# You should have received a copy of the GNU General Public License
4619-# along with this program; if not, write to the Free Software
4620-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4621-
4622-import os.path
4623-import shutil
4624-import wx
4625-import wx.lib.buttons as buttons
4626-import wx.lib.colourselect as csel
4627-
4628-from lib import consts, plugins
4629-
4630-class optionDialog(wx.Dialog):
4631- def __init__(self, *args, **kwds):
4632- kwds["style"] = wx.DEFAULT_DIALOG_STYLE
4633- wx.Dialog.__init__(self, *args, **kwds)
4634- self.SetTitle(_("Options"))
4635- self.SetIcon(wx.Icon(consts.fileIco, wx.BITMAP_TYPE_ICO))
4636- sizer = wx.BoxSizer(wx.VERTICAL)
4637-
4638- # Add notebook
4639- self.notebook = wx.Notebook(self)
4640- self.notebook_il = wx.ImageList(16, 16)
4641- self.notebook_il.Add(wx.Bitmap(consts.icon_layout))
4642- self.notebook_il.Add(wx.Bitmap(consts.icon_download))
4643- self.notebook_il.Add(wx.Bitmap(consts.icon_plugin))
4644- self.notebook.SetImageList(self.notebook_il)
4645- sizer.Add(self.notebook, 1, wx.EXPAND|wx.TOP|wx.RIGHT|wx.LEFT, 5)
4646-
4647- # Add line
4648- line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL)
4649- sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT|wx.TOP, 5)
4650-
4651- # Add buttons
4652- btnsizer = wx.StdDialogButtonSizer()
4653- btn = wx.Button(self, wx.ID_OK)
4654- btn.SetDefault()
4655- btnsizer.AddButton(btn)
4656- btn = wx.Button(self, wx.ID_CANCEL)
4657- btnsizer.AddButton(btn)
4658- btnsizer.Realize()
4659- sizer.Add(btnsizer, 0, wx.ALIGN_BOTTOM|wx.ALIGN_RIGHT|wx.ALL, 5)
4660-
4661- self.addTabs()
4662- self.SetSizer(sizer)
4663- self.Fit()
4664-
4665- def addTabs(self):
4666- pane = wx.Panel(self.notebook, -1, style=wx.TAB_TRAVERSAL)
4667- vert = wx.BoxSizer(wx.VERTICAL)
4668-
4669- txt = wx.StaticText(pane, -1, _("All directories should be relative paths."))
4670- vert.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4671-
4672- sizer = wx.BoxSizer()
4673- txt = wx.StaticText(pane, -1, _("Log Directory:"))
4674- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4675- self.logCtrl = wx.TextCtrl(pane, -1, consts.LogPath, size=wx.Size(250,-1))
4676- sizer.Add(self.logCtrl, 1, wx.ALL, 3)
4677- vert.Add(sizer, 0, wx.EXPAND)
4678-
4679- sizer = wx.BoxSizer()
4680- txt = wx.StaticText(pane, -1, _("Locale Directory:"))
4681- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4682- self.localeCtrl = wx.TextCtrl(pane, -1, consts.LocalePath)
4683- sizer.Add(self.localeCtrl, 1, wx.ALL, 3)
4684- vert.Add(sizer, 0, wx.EXPAND)
4685-
4686- #sizer = wx.BoxSizer()
4687- #txt = wx.StaticText(pane, -1, _("Packages Directory:"))
4688- #sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4689- #self.packagesCtrl = wx.TextCtrl(pane, -1, consts.PackagesPath)
4690- #sizer.Add(self.packagesCtrl, 1, wx.ALL, 3)
4691- #vert.Add(sizer, 0, wx.EXPAND)
4692-
4693- sizer = wx.BoxSizer(wx.HORIZONTAL)
4694- txt = wx.StaticText(pane, -1, _("Plugins Directory:"))
4695- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4696- self.pluginsCtrl = wx.TextCtrl(pane, -1, consts.PluginsPath)
4697- sizer.Add(self.pluginsCtrl, 1, wx.ALL, 3)
4698- vert.Add(sizer, 0, wx.EXPAND)
4699-
4700- sizer = wx.BoxSizer(wx.HORIZONTAL)
4701- txt = wx.StaticText(pane, -1, _("Projects Directory:"))
4702- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4703- self.projectsCtrl = wx.TextCtrl(pane, -1, consts.ProjectsPath)
4704- sizer.Add(self.projectsCtrl, 1, wx.ALL, 3)
4705- vert.Add(sizer, 0, wx.EXPAND)
4706-
4707- sizer = wx.BoxSizer(wx.HORIZONTAL)
4708- txt = wx.StaticText(pane, -1, _("Themes Directory:"))
4709- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4710- self.pixmapsCtrl = wx.TextCtrl(pane, -1, consts.PixmapsPath)
4711- sizer.Add(self.pixmapsCtrl, 1, wx.ALL, 3)
4712- vert.Add(sizer, 0, wx.EXPAND)
4713-
4714- sizer = wx.BoxSizer(wx.HORIZONTAL)
4715- txt = wx.StaticText(pane, -1, _("Themes Directory:"))
4716- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4717- self.themesCtrl = wx.TextCtrl(pane, -1, consts.ThemesPath)
4718- sizer.Add(self.themesCtrl, 1, wx.ALL, 3)
4719- vert.Add(sizer, 0, wx.EXPAND)
4720-
4721- sizer = wx.BoxSizer(wx.HORIZONTAL)
4722- txt = wx.StaticText(pane, -1, _("Default Theme Directory:"))
4723- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4724- self.defaultThemeCtrl = wx.TextCtrl(pane, -1, consts.ThemeDefaultPath)
4725- sizer.Add(self.defaultThemeCtrl, 1, wx.ALL, 3)
4726- vert.Add(sizer, 0, wx.EXPAND)
4727-
4728- pane.SetSizer(vert)
4729- self.notebook.AddPage(pane, _("Directories"), False, 0)
4730-
4731- ### Second page ### THIS PAGE IS USED FOR THE PROXY
4732- pane = wx.Panel(self.notebook, -1, style=wx.TAB_TRAVERSAL)
4733- vert = wx.BoxSizer(wx.VERTICAL)
4734- # vert keeps track of the vertical objects in the page (rows)
4735-
4736- self.proxyCheckBox= wx.CheckBox(pane, -1, "Enable proxy support")
4737- self.proxyCheckBox.SetValue(consts.proxy_enabled) # Set proxy value
4738- vert.Add(self.proxyCheckBox, 0, wx.ALL, 3)
4739-
4740- # Make a bold font.
4741- bold_font = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD)#, False, u'Comic Sans MS')
4742-
4743- sizer = wx.BoxSizer(wx.HORIZONTAL)
4744- # Add the "URL:" static text.
4745- url = wx.StaticText(pane, -1, _("URL: "))
4746- url.SetFont(bold_font)
4747- sizer.Add(url, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4748- # Add the "http://" static text.
4749- http = wx.StaticText(pane, -1, _("http://"))
4750- sizer.Add(http, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4751- # Add the URL text box.
4752- self.proxy_url = wx.TextCtrl(pane, -1, '')
4753- sizer.Add(self.proxy_url, 1, wx.ALL, 3)
4754- # Add the ":" static text.
4755- colon = wx.StaticText(pane, -1, _(":"))
4756- sizer.Add(colon, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4757- # Add the port text box.
4758- self.proxy_port = wx.TextCtrl(pane, -1, '')
4759- sizer.Add(self.proxy_port, 1, wx.ALL, 3)
4760- vert.Add(sizer, 0, wx.EXPAND)
4761-
4762- sizer = wx.BoxSizer(wx.HORIZONTAL)
4763- txt = wx.StaticText(pane, -1, _("Username:"))
4764- txt.SetFont(bold_font)
4765- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4766- self.proxy_username = wx.TextCtrl(pane, -1, consts.proxy_username)
4767- sizer.Add(self.proxy_username, 1, wx.ALL, 3)
4768- vert.Add(sizer, 0, wx.EXPAND)
4769-
4770- sizer = wx.BoxSizer(wx.HORIZONTAL)
4771- txt = wx.StaticText(pane, -1, _("Password:"))
4772- txt.SetFont(bold_font)
4773- sizer.Add(txt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
4774- self.proxy_password = wx.TextCtrl(pane, -1, consts.proxy_password, style=wx.TE_PASSWORD)
4775- sizer.Add(self.proxy_password, 1, wx.ALL, 3)
4776- vert.Add(sizer, 0, wx.EXPAND)
4777-
4778- pane.SetSizer(vert)
4779- self.notebook.AddPage(pane, _("Download Options"), False, 1)
4780-
4781- self.LoadProxy()
4782- self.OnChecked(None) # Enable/Disable proxy textbox based on checkbox status
4783- self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.proxyCheckBox)
4784-
4785- ### Third page ###
4786- pane = wx.Panel(self.notebook, -1, style=wx.TAB_TRAVERSAL)
4787- sizer = wx.BoxSizer(wx.VERTICAL)
4788-
4789- txt = wx.StaticText(pane, -1, _("Loaded plugins:"))
4790- txt.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
4791- sizer.Add(txt, 0, wx.ALL, 3)
4792-
4793- self.pluginList = wx.ListBox(pane)
4794- sizer.Add(self.pluginList, 1, wx.EXPAND, 0)
4795-
4796- self.fill()
4797-
4798- sizer2 = wx.BoxSizer(wx.HORIZONTAL)
4799- self.disableBtn = buttons.GenBitmapTextButton(pane, -1, wx.Bitmap(consts.icon_plugin_disable), _("Disable"))#, size=(120, 25))
4800- sizer2.Add(self.disableBtn, 0, wx.ALL, 3)
4801- self.Bind(wx.EVT_BUTTON, self.OnDisable, self.disableBtn)
4802-
4803- #bmp = wx.Bitmap(os.path.join(consts.dirPixmaps, 'plugin_go.png'))
4804- #self.disableBtn = buttons.GenBitmapTextButton(pane, -1, bmp, _("Start"))#, size=(120, 25))
4805- #sizer2.Add(self.disableBtn, 0, wx.ALL, 3)
4806-
4807- self.addBtn = buttons.GenBitmapTextButton(pane, -1, wx.Bitmap(consts.icon_plugin_add), _("Add"))#, size=(120, 25))
4808- sizer2.Add(self.addBtn, 0, wx.ALL, 3)
4809- self.Bind(wx.EVT_BUTTON, self.OnAdd, self.addBtn)
4810-
4811- #bmp = wx.Bitmap(os.path.join(consts.dirPixmaps, 'plugin_edit.png'))
4812- #self.disableBtn = buttons.GenBitmapTextButton(pane, -1, bmp, _("Edit"))#, size=(120, 25))
4813- #sizer2.Add(self.disableBtn, 0, wx.ALL, 3)
4814-
4815- #bmp = wx.Bitmap(os.path.join(consts.dirPixmaps, 'plugin_delete.png'))
4816- #self.disableBtn = buttons.GenBitmapTextButton(pane, -1, bmp, _("Delete"))#, size=(120, 25))
4817- #sizer2.Add(self.disableBtn, 0, wx.ALL, 3)
4818-
4819- sizer.Add(sizer2)
4820- pane.SetSizer(sizer)
4821- self.notebook.AddPage(pane, _("Plugins"), False, 2)
4822-
4823- def OnChecked(self, evt):
4824- if self.proxyCheckBox.GetValue():
4825- self.proxy_url.Enable()
4826- self.proxy_port.Enable()
4827- self.proxy_username.Enable()
4828- self.proxy_password.Enable()
4829- else:
4830- self.proxy_url.Disable()
4831- self.proxy_port.Disable()
4832- self.proxy_username.Disable()
4833- self.proxy_password.Disable()
4834-
4835- def LoadProxy(self):
4836- """So right here, we've got to grab the value from the config and set it in the interface"""
4837- try:
4838- url = consts.http_proxy['http']
4839- (url, port) = (url.split(':')[1][2:], url.split(':')[-1])
4840- self.proxy_url.SetValue(url)
4841- self.proxy_port.SetValue(port)
4842- except:
4843- pass
4844-
4845- def fill(self): # Fill plugin list
4846- self.pluginList.Clear()
4847- for item in plugins.OSPluginList: self.pluginList.Append(item[0])
4848- for item in plugins.InterfacePluginList: self.pluginList.Append(item[0])
4849-
4850- def OnAdd(self, evt):
4851- dlg = wx.FileDialog(self, message=_("Choose a file"),
4852- defaultDir = consts.cwd,
4853- defaultFile = "",
4854- wildcard = consts.wildcard_plugin,
4855- style=wx.OPEN | wx.CHANGE_DIR)
4856-
4857- # Show the dialog and retrieve the user response. If it is the OK response,
4858- # process the data
4859- if dlg.ShowModal() == wx.ID_OK:
4860- file = dlg.GetPaths()
4861- dlg.Destroy()
4862- if file[0]:
4863- # Copy the file to the plugins directory
4864- shutil.copyfile(file[0], os.path.join(consts.dirPlugins, os.path.basename(file[0])))
4865- else:
4866- return
4867-
4868- wx.MessageBox(_("Plugin installed successfully. Restart Keryx to load the plugin."), _("Install Successful"))
4869-
4870- def OnDisable(self, evt):
4871- def disable(instance):
4872- try:
4873- instance.cleanup()
4874- wx.MessageBox(_("Plugin disabled successfully."))
4875- return True
4876- except: return False
4877-
4878- temp = []
4879- for item in plugins.OSPluginList:
4880- if item[0] == self.pluginList.Items[self.pluginList.GetSelection()]:
4881- if disable(item[1]): # Successfully disabled
4882- temp = []
4883- for i in plugins.OSPluginList:
4884- if i[0] != item[0]: temp.append(i)
4885- plugins.OSPluginList = temp
4886- self.fill()
4887- return
4888-
4889- for item in plugins.InterfacePluginList:
4890- if item[0] == self.pluginList.Items[self.pluginList.GetSelection()]:
4891- if disable(item[1]): # Successfully disabled
4892- for i in plugins.InterfacePluginList:
4893- if i[0] != item[0]: temp.append(i)
4894- plugins.InterfacePluginList = temp
4895- self.fill()
4896- return
4897-
4898- wx.MessageBox(_("Unable to find plugin."))
4899- return False
4900-
4901-# end of class optionDialog
4902-
4903-
4904
4905=== modified file 'lib/wxkeryx/startDialog.py'
4906--- lib/wxkeryx/startDialog.py 2010-10-23 18:57:40 +0000
4907+++ lib/wxkeryx/startDialog.py 2017-06-11 16:11:12 +0000
4908@@ -16,163 +16,141 @@
4909 # along with this program; if not, write to the Free Software
4910 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4911
4912-import lib.project, os.path, platform, wx
4913+import os.path
4914+import platform
4915+import wx
4916 import wx.lib.buttons as buttons
4917-from lib import consts, log, plugins
4918-from lib.project import Project
4919-
4920-class startDialog(wx.Dialog):
4921+
4922+import lib.project
4923+from lib.project import Project, ProjectExistsException
4924+from lib import config
4925+from lib import debian
4926+
4927+from gettext import gettext as _
4928+
4929+
4930+class StartDialog(wx.Dialog):
4931 """Startup dialog"""
4932- def __init__(self, *args, **kwds):
4933- kwds["style"] = wx.DEFAULT_DIALOG_STYLE
4934- wx.Dialog.__init__(self, *args, **kwds)
4935- self.SetIcon(wx.Icon(consts.fileIco, wx.BITMAP_TYPE_ICO))
4936- self.bitmap = wx.StaticBitmap(self, -1, wx.Bitmap(consts.fileLogo))
4937- self.welcomeText = wx.StaticText(self, -1, consts.welcome_message)
4938- self.newText = wx.StaticText(self, -1, _("To get started, create a new project on your offline computer."))
4939- self.nameTextCtrl = wx.TextCtrl(self, -1, platform.node())#, size=(120,20))
4940- self.choice = wx.Choice(self, -1, choices=[], style=wx.CB_SORT)
4941-
4942- # Append the plugins into the combo box
4943- for name, instance, type, ver in plugins.OSPluginList:
4944- self.choice.Append(name)
4945- self.choice.Select(0)
4946-
4947- self.newProjectButton = buttons.GenBitmapTextButton(self, -1, wx.Bitmap(consts.icon_new), _("New Project"))#, size=(110, 25))
4948+ def __init__(self):
4949+ self.theme = config.theme
4950+ wx.Dialog.__init__(self, None, -1, _('Welcome to ') + config.name)
4951+ self.SetIcon(wx.Icon(config.ico_path, wx.BITMAP_TYPE_ICO))
4952+ self.bitmap = wx.StaticBitmap(self, -1, wx.Bitmap(config.logo_path))
4953+ self.welcomeText = wx.StaticText(self,
4954+ -1,
4955+ _('Welcome to ') + config.name)
4956+ self.welcomeText.SetFont(wx.Font(8,
4957+ wx.FONTFAMILY_DEFAULT,
4958+ wx.FONTSTYLE_NORMAL,
4959+ wx.FONTWEIGHT_BOLD,
4960+ 0,
4961+ ''))
4962+ self.newText = wx.StaticText(self, -1, _('To get started, create a new '
4963+ 'project on your offline '
4964+ 'computer.'))
4965+ self.nameTextCtrl = wx.TextCtrl(self, -1, platform.node())
4966+
4967+ self.newProjectButton = \
4968+ buttons.GenBitmapTextButton(self,
4969+ -1,
4970+ wx.Bitmap(self.theme.icon_new),
4971+ _('New Project'))
4972 self.static_line = wx.StaticLine(self, -1)
4973- self.openTxt = wx.StaticText(self, -1, _("Open a project:"))
4974- self.openProjBox = wx.Choice(self, -1, choices=[_('Browse...')], style=wx.CB_SORT)
4975+ self.openTxt = wx.StaticText(self, -1, _('Open a project:'))
4976+ self.openProjectBox = wx.Choice(self,
4977+ -1,
4978+ choices=[_('Browse...')],
4979+ style=wx.CB_SORT)
4980
4981 self.projects = []
4982- for root, dirs, files in os.walk(consts.dirProjects):
4983- for item in files:
4984- if item.endswith(consts.appFileExt):
4985- self.openProjBox.Append(item[:-6])
4986- self.projects.append(os.path.join(root, item))
4987+ for thing in os.listdir(config.projects_dir):
4988+ if os.path.isdir(os.path.join(config.projects_dir, thing)):
4989+ self.openProjectBox.Append(thing)
4990+ self.projects.append(os.path.join(config.projects_dir, thing))
4991
4992- try:
4993- self.openProjBox.Select(1)
4994- except:
4995- self.openProjBox.Select(0)
4996-
4997- self.openProjectButton = buttons.GenBitmapTextButton(self, -1, wx.Bitmap(consts.icon_open), _("Open Project"))#, size=(120, 25))
4998-
4999- self.SetTitle(consts.welcome_message)
5000- self.welcomeText.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches