summaryrefslogtreecommitdiff
path: root/portato/gui/utils.py
blob: 661af6bb05d2573f21e28e30b5762c2b2393a86a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# -*- coding: utf-8 -*-
#
# File: portato/gui/utils.py
# This file is part of the Portato-Project, a graphical portage-frontend.
#
# Copyright (C) 2006-2008 René 'Necoro' Neumann
# This is free software.  You may redistribute copies of it under the terms of
# the GNU General Public License version 2.
# There is NO WARRANTY, to the extent permitted by law.
#
# Written by René 'Necoro' Neumann <necoro@necoro.net>

from __future__ import absolute_import, with_statement

# some stuff needed
import re
import sys
import logging
import gettext
from collections import defaultdict
from threading import Thread, RLock
from functools import wraps

import gtk

# some backend things
from ..backend import flags, system, set_system
from ..helper import debug, info, set_log_level 
from ..constants import APP, LOCALE_DIR

# parser
from ..config_parser import ConfigParser

def get_color (cfg, name):
    return gtk.gdk.color_parse("#%s" % cfg.get(name, section = "COLORS"))

class GtkThread (Thread):
    def run(self):
        # for some reason, I have to install this for each thread ...
        gettext.install(APP, LOCALE_DIR, unicode = True)
        try:
            Thread.run(self)
        except SystemExit:
            raise # let normal thread handle it
        except:
            type, val, tb = sys.exc_info()
            try:
                sys.excepthook(type, val, tb, thread = self.getName())
            except TypeError:
                raise type, val, tb # let normal thread handle it
            finally:
                del type, val, tb

class Config (ConfigParser):
    
    def __init__ (self, cfgFile):
        """Constructor.

        @param cfgFile: path to config file
        @type cfgFile: string"""

        ConfigParser.__init__(self, cfgFile)
        
        # read config
        self.parse()

        # local configs
        self.local = {}

    def modify_flags_config (self):
        """Sets the internal config of the L{flags}-module.
        @see: L{flags.set_config()}"""

        flagCfg = {
                "usefile": self.get("useFile"), 
                "usePerVersion" : self.get_boolean("usePerVersion"),
                "maskfile" : self.get("maskFile"),
                "maskPerVersion" : self.get_boolean("maskPerVersion"),
                "testingfile" : self.get("keywordFile"),
                "testingPerVersion" : self.get_boolean("keywordPerVersion")}
        flags.set_config(flagCfg)

    def modify_debug_config (self):
        if self.get_boolean("debug"):
            level = logging.DEBUG
        else:
            level = logging.INFO

        set_log_level(level)

    def modify_system_config (self):
        """Sets the system config.
        @see: L{backend.set_system()}"""
        set_system(self.get("system"))

    def modify_external_configs (self):
        """Convenience function setting all external configs."""
        self.modify_debug_config()
        self.modify_flags_config()
        self.modify_system_config()

    def set_local(self, cpv, name, val):
        """Sets some local config.

        @param cpv: the cpv describing the package for which to set this option
        @type cpv: string (cpv)
        @param name: the option's name
        @type name: string
        @param val: the value to set
        @type val: any"""
        
        if not cpv in self.local:
            self.local[cpv] = {}

        self.local[cpv].update({name:val})

    def get_local(self, cpv, name):
        """Returns something out of the local config.

        @param cpv: the cpv describing the package from which to get this option
        @type cpv: string (cpv)
        @param name: the option's name
        @type name: string
        @return: value stored for the cpv and name or None if not found
        @rtype: any"""

        if not cpv in self.local:
            return None
        if not name in self.local[cpv]:
            return None

        return self.local[cpv][name]

    def write(self):
        """Writes to the config file and modify any external configs."""
        ConfigParser.write(self)
        self.modify_external_configs()

class PkgData (object):
    __slots__ = ("cat", "pkg", "inst")

    def __init__ (self, cat, pkg, inst):
        self.cat = cat
        self.pkg = pkg
        self.inst = inst

    def __iter__ (self):
        return iter((self.cat, self.pkg, self.inst))

    def __cmp__ (self, other):
        return cmp(self.pkg.lower(), other.pkg.lower())

    def __repr__ (self):
        return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst}

class Database (object):
    """An internal database which holds a simple dictionary cat -> [package_list]."""

    ALL = _("ALL")

    def __init__ (self):
        """Constructor."""
        self.__initialize()
        self._lock = RLock()

    def lock (f):
        @wraps(f)
        def wrapper (self, *args, **kwargs):
            with self._lock:
                r = f(self, *args, **kwargs)
            return r
        
        return wrapper

    def __initialize (self):
        self._db = defaultdict(list)
        self.inst_cats = set([self.ALL])
        self._restrict = None

    def __sort_key (self, x):
        return x.pkg.lower()

    @lock
    def populate (self, category = None):
        """Populates the database.
        
        @param category: An optional category - so only packages of this category are inserted.
        @type category: string
        """
        
        # get the lists
        packages = system.find_packages(category, with_version = False)
        installed = system.find_packages(category, system.SET_INSTALLED, with_version = False)
        
        # cycle through packages
        for p in packages:
            cat, pkg = p.split("/")
            inst = p in installed
            t = PkgData(cat, pkg, inst)
            self._db[cat].append(t)
            self._db[self.ALL].append(t)

            if inst:
                self.inst_cats.add(cat)

        for key in self._db: # sort alphabetically
            self._db[key].sort(key = self.__sort_key)

    @lock
    def get_cat (self, cat = None, byName = True):
        """Returns the packages in the category.
        
        @param cat: category to return the packages from; if None it defaults to "ALL"
        @type cat: string
        @param byName: selects whether to return the list sorted by name or by installation
        @type byName: boolean
        @return: an iterator over a list of tuples: (category, name, is_installed) or []
        @rtype: (string, string, boolean)<iterator>
        """
        
        if not cat:
            cat = self.ALL

        def get_pkgs():
            if byName:
                for pkg in self._db[cat]:
                    yield pkg
            else:
                ninst = []
                for pkg in self._db[cat]:
                    if pkg.inst:
                        yield pkg
                    else:
                        ninst.append(pkg)

                for pkg in ninst:
                    yield pkg

        try:
            if self.restrict:
                return (pkg for pkg in get_pkgs() if self.restrict.search(pkg.pkg))#if pkg[1].find(self.restrict) != -1)
            else:
                return get_pkgs()

        except KeyError: # cat is in category list - but not in portage
            info(_("Catched KeyError => %s seems not to be an available category. Have you played with rsync-excludes?"), cat)

    @lock
    def get_categories (self, installed = False):
        """Returns all categories.
        
        @param installed: Only return these with at least one installed package.
        @type installed: boolean
        @returns: the list of categories
        @rtype: string<iterator>
        """

        if not self.restrict:
            if installed:
                cats = self.inst_cats
            else:
                cats = self._db.iterkeys()

        else:
            if installed:
                cats = set((pkg.cat for pkg in self.get_cat(self.ALL) if pkg.inst))
            else:
                cats = set((pkg.cat for pkg in self.get_cat(self.ALL)))

            if len(cats)>1:
                cats.add(self.ALL)

        return (cat for cat in cats)

    @lock
    def reload (self, cat = None):
        """Reloads the given category.
        
        @param cat: category
        @type cat: string
        """

        if cat:
            del self._db[cat]
            try:
                self.inst_cats.remove(cat)
            except KeyError: # not in inst_cats - can be ignored
                pass
            
            self._db[self.ALL] = filter(lambda x: x.cat != cat, self._db[self.ALL])
            self.populate(cat+"/*")
        else:
            self.__initialize()
            self.populate()

    def get_restrict (self):
        return self._restrict

    @lock
    def set_restrict (self, restrict):
        if not restrict:
            self._restrict = None
        else:
            try:
                regex = re.compile(restrict, re.I)
            except re.error, e:
                info(_("Error while compiling search expression: '%s'."), str(e))
            else: # only set self._restrict if no error occurred
                self._restrict = regex

    restrict = property(get_restrict, set_restrict)