23
23
from shell import SubprocessShell
24
24
import ep_exceptions
26
from handy import get_first_text
27
28
from const import PIGMENT_TYPES
28
29
import managepigments
33
33
QUERY_OVERWRITE = 1
34
34
QUERY_OVERWRITE_DATA = 2
37
"""The state that the pigment initializes in"""
39
"""the pigment only contains essential meta-data, for example, pigments
40
viewed in the repo browser are in this state, no attachments"""
38
41
STATE_INSTALLED = 3
42
"""the pigment has been installed in its installationDirectory, all
43
necessary attachments are saved"""
45
"""the pigment has been extracted to a temporary directory, including
40
47
STATE_INCOMPLETE = 1
41
#STATE_EMPTY: this is the state that the pigment initializes in
42
#STATE_INFO: the pigment only contains essential meta-data, for example, pigments
43
# viewed in the repo browser are in this state, no attachments
44
#STATE_INSTALLED: the pigment has been installed in its installationDirectory,
45
# all necessary attachments are saved
46
#STATE_TEMP: the pigment has been extracted to a temporary directory,
47
# including attachments
48
#STATE_INCOMPLETE: the pigment doesn't contain enough meta-data to qualify
49
# as STATE_INFO, but it is not empty"""
48
"""the pigment doesn't contain enough meta-data to qualify as STATE_INFO,
49
but it is not empty"""
51
51
class PigmentType():
52
52
"""The class for the type of pigment, has the following
53
53
attributes: humanName, humanPluralName, codeName, abbreviation,
55
humanName: human readable and translatable capitalised name of the pigment
57
humanPluralName: similar to humanName, but in the plural
58
This class is an "abstract" class, it's meant to be subclassed"""
56
humanName -- human readable and translatable capitalised name of the
58
humanPluralName -- similar to humanName, but in the plural
60
This class is an abstract class, it's meant to be subclassed
59
63
humanName = {"en":""}
60
64
humanPluralName = {"en":""}
86
90
def does_system_support_me(klass):
87
"""Returns whether the system can install and activate this type
88
of pigment. For example, not all systems have Xsplash installed."""
91
"""Return whether the system can install and activate this type
92
of pigment. For example, not all systems have Xsplash installed.
100
106
def __init__(self):
101
107
"""Initilises Psigment object, including the humanName, codeName, copyright,
102
108
author, authorContact, description, preview, listStore,
103
installationDirectory and type attributes"""
109
installationDirectory and type attributes
104
112
# TODO: add version
105
113
self.humanName = {"en":""}
106
114
self.codeName = "" #: moohoo
124
132
def set_human_name(self, humanName, language="en"):
125
133
"""Set a human name for this pigment in specified language,
127
141
if len(language) == 0:
129
143
self.humanName[language] = humanName
137
151
def set_description(self, description, language="en"):
138
152
"""Set a human readable description for this pigment in specified language,
140
156
self.description[language] = description
142
158
def get_description(self, language="en"):
143
159
"""Get the human readable description for this pigment in specified
145
163
return self.description[language]
147
165
def add_attachment(self, attachment):
148
166
"""Add a file or a directory that may be needed for the installation
149
167
and activation of the pigment. When saved, the file or directory will
150
168
be found under data in the pigment compressed archive.
151
170
Please note that the order of attachments cannot be relied upon.
152
171
Use this function instead of appending to self.attachments directly.
153
173
If attachment is not an absolute path, the pigment will look for it
154
automatically based on installationDirectory"""
174
automatically based on installationDirectory.
155
177
if not os.path.exists(attachment):
156
178
raise(ep_exceptions.AttachmentNotFoundException(attachment, pigment=self))
157
179
self.attachments.append(attachment)
160
182
def get_state(self):
161
"""Returns the state of the pigment, a number corresponding to one of
183
"""Return the state of the pigment, a number corresponding to one of
163
186
STATE_EMPTY: this is the state that the pigment initializes in
164
187
STATE_INFO: the pigment only contains essential meta-data, for example, pigments
165
188
viewed in the repo browser are in this state, no attachments
168
191
STATE_TEMP: the pigment has been extracted to a temporary directory,
169
192
including attachments
170
193
STATE_INCOMPLETE: the pigment doesn't contain enough meta-data to qualify
171
as STATE_INFO, but it is not empty"""
194
as STATE_INFO, but it is not empty
172
197
# check to see if pigment's essential meta-data is completed
173
198
# only skins are excluded from the preview check
174
199
if self.humanName != {"en":""} and \
224
249
def set_preview_file(self, filename):
225
250
"""Set the preview file. If an absolute path is given, the
226
pigment will load it"""
251
pigment will load it.
227
257
if os.path.isabs(os.path.expanduser(filename)):
228
258
if not os.path.exists(filename):
229
259
raise(ep_exceptions.PreviewFileNotFoundException(filename))
237
267
self.preview = None
239
269
def _give_preview_file(self):
240
"""Returns the absolute path to a preview file for this pigment.
270
"""Return the absolute path to a preview file for this pigment.
241
271
If the preview filename hasn't been set, but a Pixbuf has, this
242
will save a temporary file named preview.png and return its path"""
272
will save a temporary file named preview.png and return its path.
243
275
subprocess.call(["mkdir", "-p", "/tmp/epidermis/preview"])
244
276
if len(self.previewFilename) > 0 and len(self.previewPath) == 0:
245
277
self.preview.save("/tmp/epidermis/preview" + self.previewFilename, self.previewFilename.split(".")[-1].lower())
269
301
def generate_preview(self):
270
"""Automatically generate a preview pixbuf based on attachments and save it to self.preview
302
"""Automatically generate a preview pixbuf based on attachments
303
and save it to self.preview.
271
304
Returning None means the method was not successful, otherwise, the method will return
272
the newly generated self.preview"""
305
the newly generated self.preview
275
310
def _extract_in_temp(self, filePath, shell=None):
276
"""Extracts the downloaded pigment to /tmp/epidermis/pigments/TYPE/CODENAME
311
"""Extract the downloaded pigment to /tmp/epidermis/pigments/TYPE/CODENAME
277
314
Returns the directory where the pigment has been extracted to.
278
filePath: the path of the downloaded .pigment (string)
279
shell: the Shell object that should be used, defaults to subprocess shell"""
317
filePath -- the path of the downloaded .pigment (string)
318
shell -- the Shell object that should be used, defaults to subprocess shell
280
321
if not os.path.exists(filePath):
281
322
raise(ep_exceptions.FileNotFoundException(filePath))
282
323
if shell is None:
301
342
def _rearrange_temp(self, dir, shell=None):
302
"""Moves temp dir from dir to /tmp/epidermis/pigments/TYPE/CODENAME
303
If preview file is in dir, it is also moved
343
"""Move temp dir from dir to /tmp/epidermis/pigments/TYPE/CODENAME
344
If preview file is in dir, it is also moved.
304
345
If any attachments are in /tmp, they're moved and re-added to attachments using self.add_attachment
305
346
Sets self.installationDirectory to /tmp/epidermis/pigments/
307
351
if shell is None:
308
352
shell = SubprocessShell()
346
390
def install(self, installationDirectory, progressCallback=None, shell=None):
347
"""Installs Pigment, ie, copies the information to installationDirectory/type/codename
391
"""Install Pigment, ie, copies the information to installationDirectory/type/codename
348
392
and copies the data to the appropriate place.
349
installationDirectory: which base directory should be used to install
350
the pigment (for example, MY_DATA_HOME)
351
progressCallback: a function which accept two argument: progress: a
352
float varying between 0.0 and 1.0, and query: a integer defaulting to None.
353
If the function returns True then the installation will be aborted.
354
query can be QUERY_OVERWRITE, which means that this method is asking
355
whether it can overwrite
356
shell: the Shell object that should be used to call commands as root
357
Returns whether the installation was successfull, True or False"""
394
Returns whether the installation was successfull, True or False
397
installationDirectory -- which base directory should be used to install
398
the pigment (for example, MY_DATA_HOME)
399
progressCallback -- a function which accept two argument: progress: a
400
float varying between 0.0 and 1.0, and query:
401
a integer defaulting to None.
402
If the function returns True then the
403
installation will be aborted.
404
query can be QUERY_OVERWRITE, which means
405
that this method is asking whether it can
407
shell -- the Shell object that should be used to call commands as root
359
411
# use subprocess shell if not specified
360
412
if shell is None:
399
451
def remove(self, progressCallback=None, shell=None):
400
"""Uninstalls pigment.
401
progressCallback: a function which accept one argument: progress: a
402
float varying between 0.0 and 1.0. If the function returns True then
403
the uninstallation will be aborted
404
shell: the Shell object that should be used to call commands as root"""
452
"""Uninstall pigment.
455
progressCallback -- a function which accept one argument: progress: a
456
float varying between 0.0 and 1.0. If the
457
function returns True then the uninstallation
459
shell -- the Shell object that should be used to call commands
405
463
# use subprocess shell if not specified
406
464
if shell is None:
407
465
shell = SubprocessShell()
419
477
progressCallback(1.0)
421
479
def activate(self, progressCallback=None, shell=None):
422
"""Applies the pigment to current user, and possibly system-wide
423
progressCallback: a function which accepts one argument: progress: a
424
float varying between 0.0 and 1.0 . If the function returns True then
425
the activation will be aborted
426
shell: the Shell object that should be used to call commands as root"""
427
if not progressCallback is None:
428
progressCallback(1.0)
480
"""Apply the pigment to current user, and possibly system-wide.
483
progressCallback -- a function which accepts one argument: progress: a
484
float varying between 0.0 and 1.0 . If the
485
function returns True then the activation
487
shell -- the Shell object that should be used to call commands
431
492
def save(self, filePath, shell=None):
432
"""Saves the pigment to filename as .pigment archive"""
493
"""Save the pigment to filename as .pigment archive"""
433
494
# TODO: save needs to be more easily 'sub-classed'
434
495
# for example, the gtk_ pigment saves with it metacity
435
496
doc = self.information_xml()
467
528
def read(self, filePath):
468
529
"""Load the Pigment object from an xml file (.xml)
469
531
This function will look for the preview file and for attachments, if required
470
532
NOTE: this function will look for attachments in places other than filePath,
471
#TODO: change this?"""
472
536
if not os.path.exists(filePath):
473
537
raise(ep_exceptions.FileNotFoundException(filePath))
474
538
doc = minidom.parse(filePath)
522
586
def write(self, filePath, shell=None):
523
587
"""Write the XML file to filePath
524
Doesn't save preview"""
525
591
# TODO: fixme ? write doesn't save preview
526
592
subprocess.call(["rm", "/tmp/epidermis/pigment.xml"])
527
593
subprocess.call(["mkdir", "-p", "/tmp/epidermis"])
536
602
shell.do(["mv", "/tmp/epidermis/pigment.xml", filePath])
538
604
def information_xml(self):
539
"""Returns the XML document describing the pigment, this document is
540
usually saved as pigment.xml"""
605
"""Return the XML document describing the pigment, this document is
606
usually saved as pigment.xml
541
609
doc = minidom.Document()
542
610
root = doc.createElement("pigment")
543
611
doc.appendChild(root)
572
640
def is_attachment_correct(self, attachmentIndex=None):
573
641
"""Return whether the attachment number attachmentIndex or all the attachments
574
642
if None is passed are correct.
575
644
For example, a Metacity pigment would verify that the attachment is a
576
645
directory with a metacity-1 subdirectory
577
It does not verify the location of the attachment"""
646
It does not verify the location of the attachment
578
649
if attachmentIndex is None:
579
650
for att in self.attachments:
580
651
if not os.path.exists(att):
596
667
def summary(self):
597
"""Returns a string: summary of the Pigment object"""
668
"""Return a string: summary of the Pigment object"""
598
669
return "Pigment codename: " + self.codeName \
599
670
+ "\nhumanName: " + str(self.humanName) \
600
671
+ "\ncopyright: " + self.copyright \
625
696
def does_activation_require_root(self):
626
"""Returns whether the activation requires root rights.
697
"""Return whether the activation requires root rights.
627
698
If it does, the Pigment object will need a Shell running as root
628
to be passed to some of its methods"""
699
to be passed to some of its methods
631
704
def does_uninstallation_require_root(self):
632
"""Returns whether the activation requires root's rights.
705
"""Return whether the activation requires root's rights.
633
706
If it does, the Pigment object will need a Shell running as root
634
to be passed to the remove method"""
707
to be passed to the remove method
635
710
# TODO: check permissions of files and attachments to determine
636
711
# whether uninstallation requires root
639
714
def match_filter(self, matchString, lang="en"):
640
"""Returns whether this pigment should be a result for a search for the
715
"""Return whether this pigment should be a result for a search for the
719
matchString -- the search string
720
lang -- the language code
642
723
for ii in (self.get_human_name(lang), self.get_description(lang)):
643
724
if matchString.lower() in ii.lower():
647
get_first_text = handy.get_first_text
650
print >> sys.stderr, "don't run this file"
653
if __name__ == "__main__":