Moved gkofoto library files into a "gnomekofoto" package.
authorJoel Rosdahl <joel@rosdahl.net>
Fri, 17 Oct 2003 21:47:54 +0000 (21:47 +0000)
committerJoel Rosdahl <joel@rosdahl.net>
Fri, 17 Oct 2003 21:47:54 +0000 (21:47 +0000)
Created a setup.py and a makefile that calls it.

Made gkofoto able to run both in source directory and installed (like
in /usr/local/bin with data files in /usr/local/share/gnomekofoto).

Added installation notes to INSTALL.

34 files changed:
INSTALL
Makefile [new file with mode: 0644]
doc/todo.txt
setup.py [new file with mode: 0755]
src/gnome/albums.py [deleted file]
src/gnome/categories.py [deleted file]
src/gnome/categorydialog.py [deleted file]
src/gnome/environment.py [deleted file]
src/gnome/gkofoto
src/gnome/gnomekofoto/__init__.py [new file with mode: 0644]
src/gnome/gnomekofoto/albums.py [new file with mode: 0644]
src/gnome/gnomekofoto/categories.py [new file with mode: 0644]
src/gnome/gnomekofoto/categorydialog.py [new file with mode: 0644]
src/gnome/gnomekofoto/environment.py [new file with mode: 0644]
src/gnome/gnomekofoto/imagecontextmenu.py [new file with mode: 0644]
src/gnome/gnomekofoto/images.py [new file with mode: 0644]
src/gnome/gnomekofoto/imageselection.py [new file with mode: 0644]
src/gnome/gnomekofoto/imageview.py [new file with mode: 0644]
src/gnome/gnomekofoto/mainwindow.py [new file with mode: 0644]
src/gnome/gnomekofoto/mysortedmodel.py [new file with mode: 0644]
src/gnome/gnomekofoto/singleimageview.py [new file with mode: 0644]
src/gnome/gnomekofoto/source.py [new file with mode: 0644]
src/gnome/gnomekofoto/tableview.py [new file with mode: 0644]
src/gnome/gnomekofoto/thumbnailview.py [new file with mode: 0644]
src/gnome/imagecontextmenu.py [deleted file]
src/gnome/images.py [deleted file]
src/gnome/imageselection.py [deleted file]
src/gnome/imageview.py [deleted file]
src/gnome/mainwindow.py [deleted file]
src/gnome/mysortedmodel.py [deleted file]
src/gnome/singleimageview.py [deleted file]
src/gnome/source.py [deleted file]
src/gnome/tableview.py [deleted file]
src/gnome/thumbnailview.py [deleted file]

diff --git a/INSTALL b/INSTALL
index 7bf5139..03f92e4 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -15,3 +15,17 @@ Dependencies:
   * PySQLite. Found at:
 
         http://pysqlite.sourceforge.net
+
+Installation:
+
+    Unix/Windows:
+
+        Run "python setup.py install". See "python setup.py --help"
+        for more information. For example: To install to a different
+        location than the default (say, c:\kofoto or /usr/local), run
+        "python setup.py --prefix=<otherlocation>".
+
+    Unix (alternative):
+
+        Run "make install" (installs into /usr/local) or "make install
+        PREFIX=<prefix>".
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..17d24b0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+PREFIX = /usr/local
+
+help:
+       @echo "Available targets:"
+       @echo
+       @echo "clean"
+       @echo "install [PREFIX=prefix]"
+
+clean:
+       rm -rf build
+       find . \( -name '*~' -o -name '*.pyc' \) -exec rm -f {} \;
+
+install:
+       python setup.py install --prefix=$(PREFIX)
index c7e1878..9b32a60 100644 (file)
@@ -45,8 +45,6 @@
 * When generating an album, store images in per-month directories
   instead of the current two-level hash-based directory structure.
 
-* Write setup.py.
-
 * Write a simple, run-once client that creates a temporary shelf,
   registers images in given directories, generates an album and
   removes the shelf?
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..dbc73e1
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,19 @@
+#! /usr/bin/env python
+
+from distutils.core import setup
+setup(
+    name="kofoto",
+    version="0.0.0",
+    package_dir={
+        "": "src/lib",
+        "kofoto": "src/lib/kofoto",
+        "gnomekofoto": "src/gnome/gnomekofoto"},
+    packages=["kofoto", "gnomekofoto"],
+    py_modules=["EXIF"],
+    scripts=["src/cmdline/kofoto", "src/gnome/gkofoto"],
+    data_files=[
+        ("share/gnomekofoto/glade", ["src/gnome/glade/gkofoto.glade"]),
+        ("share/gnomekofoto/icons", ["src/gnome/icons/fullscreen-24.png"])],
+    author="Joel Rosdahl",
+    author_email="joel@rosdahl.net",
+    url="http://svn.rosdahl.net/kofoto/kofoto/")
diff --git a/src/gnome/albums.py b/src/gnome/albums.py
deleted file mode 100644 (file)
index cbcfa1a..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-import gtk
-import gobject
-import gtk
-
-from environment import env
-
-class Albums:
-    _COLUMN_ALBUM_ID  = 0
-    _COLUMN_TAG       = 1
-    _COLUMN_TYPE      = 2
-
-    _model = None
-    
-    def __init__(self, source):
-        self._model = gtk.TreeStore(gobject.TYPE_INT,      # ALBUM_ID
-                                    gobject.TYPE_STRING,   # TAG
-                                    gobject.TYPE_STRING)   # TYPE
-        albumView = env.widgets["albumView"]
-        self._source = source
-        albumView.set_model(self._model)
-        renderer = gtk.CellRendererText()
-        column = gtk.TreeViewColumn("Albums", renderer, text=self._COLUMN_TAG)
-        column.set_clickable(gtk.TRUE)
-        albumView.append_column(column)
-        albumSelection = albumView.get_selection()
-        albumSelection.connect('changed', self._albumSelectionHandler)
-        self.reload()
-        
-    def reload(self):
-        self._buildModel(None, env.shelf.getRootAlbum(), [])
-
-    def _buildModel(self, parent, object, visited):
-        for child in object.getAlbumChildren():
-            albumId = child.getId()
-            iter = self._model.insert_before(parent, None)
-            self._model.set_value(iter, self._COLUMN_ALBUM_ID, albumId)
-            self._model.set_value(iter, self._COLUMN_TYPE, child.getType())
-            if albumId not in visited:
-                self._model.set_value(iter, self._COLUMN_TAG, child.getTag())
-                self._buildModel(iter, child, visited + [albumId])
-            else:
-                # TODO: Use "set_select_funtion" and add [...] as a child instead
-                self._model.set_value(iter, self._COLUMN_TAG, child.getTag() + "  [...]")
-
-    def _albumSelectionHandler(self, selection):
-        albumModel, iter = selection.get_selected()
-        if iter:
-            albumTag = albumModel.get_value(iter, self._COLUMN_TAG)
-            self._source.set("album://" + albumTag)
diff --git a/src/gnome/categories.py b/src/gnome/categories.py
deleted file mode 100644 (file)
index 0a550f0..0000000
+++ /dev/null
@@ -1,419 +0,0 @@
-import gtk
-import gobject
-import gtk
-import string
-
-from environment import env
-from images import Images
-from categorydialog import CategoryDialog
-from kofoto.search import *
-from kofoto.shelf import *
-
-class Categories:
-    _COLUMN_CATEGORY_ID  = 0
-    _COLUMN_TAG          = 1
-    _COLUMN_DESCRIPTION  = 2
-    _COLUMN_CONNECTED    = 3
-    _COLUMN_INCONSISTENT = 4
-    _model = None
-    _toggleColumn = None
-    _ignoreSelectEvent = gtk.FALSE
-    _selectedCategories = {}
-    
-    def __init__(self, loadedImages, selectedImages, source):
-        self._model = gtk.TreeStore(gobject.TYPE_INT,      # CATEGORY_ID
-                                    gobject.TYPE_PYOBJECT, # TAG
-                                    gobject.TYPE_STRING,   # DESCRIPTION
-                                    gobject.TYPE_BOOLEAN,  # CONNECTED
-                                    gobject.TYPE_BOOLEAN)  # INCONSISTENT
-        categoryView = env.widgets["categoryView"]
-        categoryView.realize()
-        self._source = source
-        self._loadedImages = loadedImages
-        self._selectedImages = selectedImages
-        categoryView.set_model(self._model)
-
-        # Create columns
-        renderer = gtk.CellRendererToggle()
-        renderer.connect("toggled", self._connectionToggled)
-        self._toggleColumn = gtk.TreeViewColumn("",
-                                          renderer,
-                                          active=self._COLUMN_CONNECTED,
-                                          inconsistent=self._COLUMN_INCONSISTENT)
-        categoryView.append_column(self._toggleColumn)
-        
-        renderer = gtk.CellRendererText()
-        column = gtk.TreeViewColumn("Category", renderer, text=self._COLUMN_DESCRIPTION)
-        categoryView.append_column(column)
-        categoryView.set_expander_column(column)
-
-        # Create context menu
-        self._contextMenu = gtk.Menu()
-
-        self._copyItem = gtk.MenuItem("Copy")
-        self._copyItem.show()
-        self._copyItem.connect("activate", self._copyCategory, None)
-        self._contextMenu.append(self._copyItem)
-
-        self._cutItem = gtk.MenuItem("Cut")
-        self._cutItem.show()
-        self._cutItem.connect("activate", self._cutCategory, None)
-        self._contextMenu.append(self._cutItem)
-        
-        self._pasteItem = gtk.MenuItem("Paste")
-        self._pasteItem.show()
-        self._pasteItem.connect("activate", self._pasteCategory, None)
-        self._contextMenu.append(self._pasteItem)
-
-        self._createChildItem = gtk.MenuItem("Create child category")
-        self._createChildItem.show()
-        self._createChildItem.connect("activate", self._createChildCategory, None)
-        self._contextMenu.append(self._createChildItem)
-        
-        self._createRootItem = gtk.MenuItem("Create root category")
-        self._createRootItem.show()
-        self._createRootItem.connect("activate", self._createRootCategory, None)
-        self._contextMenu.append(self._createRootItem)
-        
-        self._deleteItem = gtk.MenuItem("Delete selected categories")
-        self._deleteItem.show()
-        self._deleteItem.connect("activate", self._deleteCategory, None)
-        self._contextMenu.append(self._deleteItem)
-
-        self._disconnectItem = gtk.MenuItem("Disconnect selected categories")
-        self._disconnectItem.show()
-        self._disconnectItem.connect("activate", self._disconnectCategory, None)
-        self._contextMenu.append(self._disconnectItem)
-
-        self._propertiesItem = gtk.MenuItem("Properties")
-        self._propertiesItem.show()
-        self._propertiesItem.connect("activate", self._editProperties, None)
-        self._contextMenu.append(self._propertiesItem)
-
-        self.updateContextMenu()
-
-        # Init selection functions
-        categorySelection = categoryView.get_selection()
-        categorySelection.set_mode(gtk.SELECTION_MULTIPLE)
-        categorySelection.set_select_function(self._selectionFunction, None)
-        categorySelection.connect("changed", self._selectionUpdated)
-
-        # Connect events
-        categoryView.connect("button_press_event", self._button_pressed)
-        categoryView.connect("button_release_event", self._button_released)
-        categoryView.connect("row-activated", self._rowActivated)
-                                            
-        # Connect search button
-        env.widgets["categorySearchButton"].connect('clicked', self._executeQuery)
-        
-        # Load data into model
-        self.reload()
-    
-    def reload(self):
-        self._model.clear()
-        env.shelf.flushCategoryCache()
-        for category in env.shelf.getRootCategories():
-            self._populateModel(None, category)
-
-    def _populateModel(self, parent, category):
-        iter = self._model.append(parent)
-        self._model.set_value(iter, self._COLUMN_CATEGORY_ID, category.getId())
-        self._model.set_value(iter, self._COLUMN_TAG, category.getTag())
-        self._model.set_value(iter, self._COLUMN_DESCRIPTION, category.getDescription())
-        self._model.set_value(iter, self._COLUMN_CONNECTED, gtk.FALSE)
-        self._model.set_value(iter, self._COLUMN_INCONSISTENT, gtk.FALSE)
-        for child in category.getChildren():
-            self._populateModel(iter, child)
-
-    def _executeQuery(self, *foo):
-        query = self._buildQuery()
-        if query:
-            self._source.set("query://" + query)
-            
-    def _buildQuery(self):
-        if env.widgets["categoriesOr"].get_active():
-            operator = " or "
-        else:
-            operator = " and "
-        query = ""
-        for categoryId in self._selectedCategories:
-            if query:
-                query += operator
-            category = env.shelf.getCategory(categoryId)
-            query += category.getTag()
-        return query
-        
-    def _selectionUpdated(self, selection):
-        self._selectedCategories = {}
-        self._parseCategoryTree(self._model, None, selection)
-        self.updateContextMenu()
-            
-    def _parseCategoryTree(self, categoryList, parent, selection):
-        for row in categoryList:
-            categoryId = row[self._COLUMN_CATEGORY_ID]
-            if selection.iter_is_selected(row.iter):
-                if categoryId in self._selectedCategories:
-                    self._selectedCategories[categoryId].append(parent)
-                else:
-                    self._selectedCategories[categoryId] = [parent]
-            self._parseCategoryTree(row.iterchildren(), categoryId, selection)
-            
-    def updateContextMenu(self):
-        if len(self._selectedCategories) == 0:
-            self._deleteItem.set_sensitive(gtk.FALSE)
-            self._createChildItem.set_sensitive(gtk.FALSE)
-            self._copyItem.set_sensitive(gtk.FALSE)
-            self._cutItem.set_sensitive(gtk.FALSE)
-            self._pasteItem.set_sensitive(gtk.FALSE)
-            self._disconnectItem.set_sensitive(gtk.FALSE)
-        else:
-            self._deleteItem.set_sensitive(gtk.TRUE)
-            self._createChildItem.set_sensitive(gtk.TRUE)
-            self._copyItem.set_sensitive(gtk.TRUE)
-            self._cutItem.set_sensitive(gtk.TRUE)
-            if env.controller.clipboardHasCategory():
-                self._pasteItem.set_sensitive(gtk.TRUE)
-            else:
-                self._pasteItem.set_sensitive(gtk.FALSE)
-            self._disconnectItem.set_sensitive(gtk.TRUE)
-        if len(self._selectedCategories) == 1:
-            self._propertiesItem.set_sensitive(gtk.TRUE)
-        else:
-            self._propertiesItem.set_sensitive(gtk.FALSE)
-            
-    def updateView(self, doNotAutoCollapse=gtk.FALSE):
-        nrSelectedImagesInCategory = {}
-        nrSelectedImages = 0
-        # find out which categories are connected, not connected or
-        # partitionally connected to selected images
-        for image in self._loadedImages.model:
-            imageId = image[Images.COLUMN_IMAGE_ID]
-            if imageId in self._selectedImages:
-                nrSelectedImages += 1
-                image = env.shelf.getImage(imageId)
-                for category in image.getCategories():
-                    categoryId = category.getId()
-                    try:
-                        nrSelectedImagesInCategory[categoryId] += 1
-                    except(KeyError):
-                        nrSelectedImagesInCategory[categoryId] = 1
-        pathsToExpand = self._updateViewHelper(self._model,
-                                           nrSelectedImagesInCategory,
-                                           nrSelectedImages,
-                                           doNotAutoCollapse)[0]
-        if env.widgets["autoExpand"].get_active():
-            pathsToExpand.reverse()
-            for path in pathsToExpand:
-                env.widgets["categoryView"].expand_row(path, gtk.FALSE)  
-            
-    def _updateViewHelper(self, categories, nrSelectedImagesInCategory, nrSelectedImages, doNotAutoCollapse):
-        pathsToExpand = []
-        expandParent = gtk.FALSE
-        for categoryRow in categories:
-            childPathsToExpand, expandThis = self._updateViewHelper(categoryRow.iterchildren(),
-                                                                nrSelectedImagesInCategory,
-                                                                nrSelectedImages,
-                                                                doNotAutoCollapse)
-            pathsToExpand.extend(childPathsToExpand)
-            if expandThis:
-                pathsToExpand.append(categoryRow.path)
-                expandParent = gtk.TRUE
-            if categoryRow[self._COLUMN_CATEGORY_ID] in self._selectedCategories:
-                # Dont auto collapse selected categories
-                expandParent = gtk.TRUE
-            categoryId = categoryRow[self._COLUMN_CATEGORY_ID]
-            if categoryId in nrSelectedImagesInCategory:
-                expandParent = gtk.TRUE
-                if nrSelectedImagesInCategory[categoryId] < nrSelectedImages:
-                    # Some of the selected images are connected to the category
-                    categoryRow[self._COLUMN_CONNECTED] = gtk.FALSE
-                    categoryRow[self._COLUMN_INCONSISTENT] = gtk.TRUE
-                else:
-                    # All of the selected images are connected to the category
-                    categoryRow[self._COLUMN_CONNECTED] = gtk.TRUE
-                    categoryRow[self._COLUMN_INCONSISTENT] = gtk.FALSE
-            else:
-                # None of the selected images are connected to the category
-                categoryRow[self._COLUMN_CONNECTED] = gtk.FALSE
-                categoryRow[self._COLUMN_INCONSISTENT] = gtk.FALSE
-            if (not expandThis) and (not doNotAutoCollapse) and env.widgets["autoCollapse"].get_active():
-                env.widgets["categoryView"].collapse_row(categoryRow.path)
-        return pathsToExpand, expandParent
-                
-    def _connectionToggled(self, renderer, path):
-        categoryRow = self._model[path]
-        category = env.shelf.getCategory(categoryRow[self._COLUMN_CATEGORY_ID])
-        if categoryRow[self._COLUMN_INCONSISTENT]:
-            for imageId in self._selectedImages:
-                try:
-                    env.shelf.getObject(imageId).addCategory(category)
-                except(CategoryPresentError):
-                    pass
-            categoryRow[self._COLUMN_INCONSISTENT] = gtk.FALSE
-            categoryRow[self._COLUMN_CONNECTED] = gtk.TRUE
-        elif categoryRow[self._COLUMN_CONNECTED]:
-            for imageId in self._selectedImages:
-                env.shelf.getObject(imageId).removeCategory(category)
-            categoryRow[self._COLUMN_CONNECTED] = gtk.FALSE
-        else:
-            for imageId in self._selectedImages:
-                env.shelf.getObject(imageId).addCategory(category)
-                categoryRow[self._COLUMN_CONNECTED] = gtk.TRUE
-
-    def _button_pressed(self, treeView, event):
-        if event.button == 3:
-            self._contextMenu.popup(None,None,None,event.button,event.time)
-            return gtk.TRUE
-        rec = env.widgets["categoryView"].get_cell_area(0, self._toggleColumn)
-        if event.x <= (rec.x + rec.width):
-            self._ignoreSelectEvent = gtk.TRUE
-        return gtk.FALSE
-
-    def _button_released(self, treeView, event):
-        self._ignoreSelectEvent = gtk.FALSE
-        return gtk.FALSE
-        
-    def _rowActivated(self, a, b, c):
-        print "not yet implemented!"
-
-    def _copyCategory(self, item, data):
-        cc = ClipboardCategories()
-        cc.type = cc.COPY
-        cc.categories = self._selectedCategories
-        env.controller.clipboardSet(cc)
-
-    def _cutCategory(self, item, data):
-        cc = ClipboardCategories()
-        cc.type = cc.CUT
-        cc.categories = self._selectedCategories
-        env.controller.clipboardSet(cc)
-
-    def _pasteCategory(self, item, data):
-        clipboardCategories = env.controller.clipboardPop()
-        try:
-            for (categoryId, parentIds) in clipboardCategories.categories.items():
-                for selectedCategoryId in self._selectedCategories:
-                    self._connectChild(selectedCategoryId, categoryId)
-                    self._expandCategory(selectedCategoryId, self._model)
-                if clipboardCategories.type == ClipboardCategories.CUT:
-                    for parentId in parentIds:
-                        if parentId != None:
-                            self._disconnectChild(parentId, categoryId)
-        except(CategoryLoopError):
-            print "Error: Category loop detected"
-            # TODO: Show dialog box with error message
-        self.updateView(doNotAutoCollapse = gtk.TRUE)
-
-    def _expandCategory(self, categoryId, categories):
-        for c in categories:
-            if c[self._COLUMN_CATEGORY_ID] == categoryId:
-                env.widgets["categoryView"].expand_row(c.path, gtk.FALSE)
-            self._expandCategory(categoryId, c.iterchildren())
-        
-    def _createRootCategory(self, item, data):
-        dialog = CategoryDialog("Create root category")
-        dialog.run(self._createRootCategoryHelper)
-       
-    def _createRootCategoryHelper(self, tag, desc):
-        category = env.shelf.createCategory(tag, desc)
-        self._populateModel(None, category)
-        self.updateView(doNotAutoCollapse = gtk.TRUE)
-
-    def _createChildCategory(self, item, data):
-        dialog = CategoryDialog("Create child category")
-        dialog.run(self._createChildCategoryHelper)
-
-    def _createChildCategoryHelper(self, tag, desc):
-        newCategory = env.shelf.createCategory(tag, desc)
-        for selectedCategoryId in self._selectedCategories:
-            self._connectChild(selectedCategoryId, newCategory.getId())
-        self.updateView(doNotAutoCollapse = gtk.TRUE)
-        
-    def _deleteCategory(self, item, data):
-        # TODO: Add confirmation dialog box
-        for categoryId in self._selectedCategories:
-            category = env.shelf.getCategory(categoryId)
-            for child in list(category.getChildren()):
-                # The backend automatically disconnects childs when a category
-                # is deleted, but we do it outself to make sure that the
-                # treeview widget is updated
-                self._disconnectChild(categoryId, child.getId())
-            env.shelf.deleteCategory(categoryId)
-            env.shelf.flushCategoryCache()
-            self._deleteCategoryHelper(self._model, categoryId)
-        self.updateView(doNotAutoCollapse = gtk.TRUE)
-
-    def _deleteCategoryHelper(self, categories, categoryId):
-         for c in categories:
-            if c[self._COLUMN_CATEGORY_ID] == categoryId:
-                self._model.remove(c.iter)
-            self._deleteCategoryHelper(c.iterchildren(), categoryId)
-
-    def _disconnectCategory(self, item, data):
-        for (categoryId, parentIds) in self._selectedCategories.items():
-            for parentId in parentIds:
-                if not parentId == None:
-                    self._disconnectChild(parentId, categoryId)
-        self.updateView(doNotAutoCollapse = gtk.TRUE)
-
-    def _editProperties(self, item, data):
-        for category in self._selectedCategories:
-            dialog = CategoryDialog("Change properties", category)
-            dialog.run(self._editPropertiesHelper, data=category)
-
-    def _editPropertiesHelper(self, tag, desc, categoryId):
-         category = env.shelf.getCategory(categoryId)
-         category.setTag(tag)
-         category.setDescription(desc)
-         self._updateProperties(categoryId, self._model)
-
-    def _updateProperties(self, categoryId, categories):
-        for row in categories:
-            if row[self._COLUMN_CATEGORY_ID] == categoryId:
-                c = env.shelf.getCategory(categoryId)
-                row[self._COLUMN_TAG] = c.getTag()
-                row[self._COLUMN_DESCRIPTION] = c.getDescription()
-            self._updateProperties(categoryId, row.iterchildren())
-        
-    def _selectionFunction(self, path, b):
-        if self._ignoreSelectEvent:
-            return gtk.FALSE
-        else:
-            return gtk.TRUE
-
-    def _connectChild(self, parentId, childId):
-        childCategory = env.shelf.getCategory(childId)
-        parentCategory = env.shelf.getCategory(parentId)
-        parentCategory.connectChild(childCategory)
-        self._connectChildHelper(self._model, parentId, childCategory)
-        for category in self._model:
-            if category[self._COLUMN_CATEGORY_ID] == childId:
-                self._model.remove(category.iter)
-        
-    def _connectChildHelper(self, categories, parentId, child):
-        for c in categories:
-            if c[self._COLUMN_CATEGORY_ID] == parentId:
-                self._populateModel(c.iter, child)
-            self._connectChildHelper(c.iterchildren(), parentId, child)
-
-    def _disconnectChild(self, parentId, childId):
-        childCategory = env.shelf.getCategory(childId)
-        parentCategory = env.shelf.getCategory(parentId)
-        parentCategory.disconnectChild(childCategory)
-        self._disconnectChildHelper(self._model, None, childId, parentId)
-        if len(list(childCategory.getParents())) == 0:
-            self._populateModel(None, childCategory)
-        
-    def _disconnectChildHelper(self, categories, parent, childId, parentId):
-        for c in categories:
-            id = c[self._COLUMN_CATEGORY_ID]
-            if id == childId and parent == parentId:
-                self._model.remove(c.iter)
-            self._disconnectChildHelper(c.iterchildren(), id, childId, parentId)
-
-class ClipboardCategories:
-    COPY = 1
-    CUT = 2
-    categories = None
-    type = None
-
diff --git a/src/gnome/categorydialog.py b/src/gnome/categorydialog.py
deleted file mode 100644 (file)
index dcc5406..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-import gtk
-import gobject
-import gtk
-import string
-
-from environment import env
-from kofoto.shelf import *
-
-class CategoryDialog:
-    def __init__(self, title, categoryId=None):
-        if categoryId:
-            category = env.shelf.getCategory(categoryId)
-            tagText = category.getTag()
-            descText = category.getDescription()
-        else:
-            tagText = ""
-            descText = ""
-            
-        widgets = gtk.glade.XML(env.gladeFile, "categoryProperties")
-        self._dialog = widgets.get_widget("categoryProperties")
-        self._dialog.set_title(title)
-        self._tagWidget = widgets.get_widget("tag")
-        self._tagWidget.set_text(tagText)
-        self._descWidget = widgets.get_widget("description")
-        self._descWidget.set_text(descText)
-        self._descWidget.connect("changed", self._descriptionChanged, self._tagWidget)
-        self._tagWidget.connect("changed", self._tagChanged, widgets.get_widget("okbutton"))
-
-    def run(self, ok=None, data=None):
-        result = self._dialog.run()
-        tag = self._tagWidget.get_text().decode("utf-8")
-        desc = self._descWidget.get_text().decode("utf-8")            
-        self._dialog.destroy()       
-        if result == gtk.RESPONSE_OK:
-            if ok == None:
-                return None
-            else:
-                if data:
-                    return ok(tag, desc, data)
-                else:
-                    return ok(tag, desc)
-        else:
-            return None
-    
-    def _descriptionChanged(self, description, tag):
-        # Remove all whitespaces in description and then use it as tag
-        tag.set_text(string.translate(description.get_text(),
-                                      string.maketrans("", ""),
-                                      string.whitespace))
-
-    def _tagChanged(self, tag, button):
-        tagString = tag.get_text().decode("utf-8")
-        try:
-           # Check that the tag name is valid
-           verifyValidCategoryTag(tagString)
-        except(BadCategoryTagError):
-            button.set_sensitive(gtk.FALSE)
-            return
-        try:
-            # Make sure that the tag name is not already taken
-            env.shelf.getCategory(tagString)
-            button.set_sensitive(gtk.FALSE)
-        except(CategoryDoesNotExistError):
-            button.set_sensitive(gtk.TRUE)
diff --git a/src/gnome/environment.py b/src/gnome/environment.py
deleted file mode 100644 (file)
index fb67c40..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-import sys
-import os
-import getopt
-import locale
-
-# Find libraries if installed in ../lib (like in the source tree).
-if os.path.islink(sys.argv[0]):
-    link = os.readlink(sys.argv[0])
-    absloc = os.path.normpath(
-        os.path.join(os.path.dirname(sys.argv[0]), link))
-    bindir = os.path.dirname(absloc)
-else:
-    bindir = os.path.dirname(sys.argv[0])
-sys.path.insert(0, os.path.join(bindir, "..", "lib"))
-
-from kofoto.common import *
-from kofoto.shelf import *
-from kofoto.config import *
-
-class Environment:
-     # TODO: read from configuration file?
-    defaultTableViewColumns = [u"title", u"description", u"captured"]
-    defaultThumbnailViewColumns = [u"captured"]
-    defaultSortColumn = u"captured"
-
-env = Environment()
-locale.setlocale(locale.LC_ALL, "")
-env.codeset = locale.nl_langinfo(locale.CODESET)
-conf = Config(DEFAULT_CONFIGFILE, env.codeset)
-conf.read()
-genconf = conf.getGeneralConfig()
-env.imageCacheLocation = genconf["imagecache_location"]
-env.imageSizes = genconf["image_sizes"]
-env.baseDir = bindir
-env.iconDir = bindir + "/icons/"
-env.gladeFile = env.baseDir + "/glade/gkofoto.glade"
-env.shelf = Shelf(genconf["shelf_location"], env.codeset)
index 20f3c38..615ebc8 100755 (executable)
@@ -7,20 +7,20 @@ import gobject
 import gtk.glade
 import gnome
 import gnome.ui
-from environment import env
+from gnomekofoto.environment import env
 
 from kofoto.imagecache import *
 
-from imageselection import *
-from imagecontextmenu import *
-from albums import *
-from images import *
-from mainwindow import *
-from tableview import *
-from thumbnailview import *
-from singleimageview import *
-from categories import *
-from source import *
+from gnomekofoto.imageselection import *
+from gnomekofoto.imagecontextmenu import *
+from gnomekofoto.albums import *
+from gnomekofoto.images import *
+from gnomekofoto.mainwindow import *
+from gnomekofoto.tableview import *
+from gnomekofoto.thumbnailview import *
+from gnomekofoto.singleimageview import *
+from gnomekofoto.categories import *
+from gnomekofoto.source import *
 
 ######################################################################
 ### Classes
diff --git a/src/gnome/gnomekofoto/__init__.py b/src/gnome/gnomekofoto/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/gnome/gnomekofoto/albums.py b/src/gnome/gnomekofoto/albums.py
new file mode 100644 (file)
index 0000000..cbcfa1a
--- /dev/null
@@ -0,0 +1,49 @@
+import gtk
+import gobject
+import gtk
+
+from environment import env
+
+class Albums:
+    _COLUMN_ALBUM_ID  = 0
+    _COLUMN_TAG       = 1
+    _COLUMN_TYPE      = 2
+
+    _model = None
+    
+    def __init__(self, source):
+        self._model = gtk.TreeStore(gobject.TYPE_INT,      # ALBUM_ID
+                                    gobject.TYPE_STRING,   # TAG
+                                    gobject.TYPE_STRING)   # TYPE
+        albumView = env.widgets["albumView"]
+        self._source = source
+        albumView.set_model(self._model)
+        renderer = gtk.CellRendererText()
+        column = gtk.TreeViewColumn("Albums", renderer, text=self._COLUMN_TAG)
+        column.set_clickable(gtk.TRUE)
+        albumView.append_column(column)
+        albumSelection = albumView.get_selection()
+        albumSelection.connect('changed', self._albumSelectionHandler)
+        self.reload()
+        
+    def reload(self):
+        self._buildModel(None, env.shelf.getRootAlbum(), [])
+
+    def _buildModel(self, parent, object, visited):
+        for child in object.getAlbumChildren():
+            albumId = child.getId()
+            iter = self._model.insert_before(parent, None)
+            self._model.set_value(iter, self._COLUMN_ALBUM_ID, albumId)
+            self._model.set_value(iter, self._COLUMN_TYPE, child.getType())
+            if albumId not in visited:
+                self._model.set_value(iter, self._COLUMN_TAG, child.getTag())
+                self._buildModel(iter, child, visited + [albumId])
+            else:
+                # TODO: Use "set_select_funtion" and add [...] as a child instead
+                self._model.set_value(iter, self._COLUMN_TAG, child.getTag() + "  [...]")
+
+    def _albumSelectionHandler(self, selection):
+        albumModel, iter = selection.get_selected()
+        if iter:
+            albumTag = albumModel.get_value(iter, self._COLUMN_TAG)
+            self._source.set("album://" + albumTag)
diff --git a/src/gnome/gnomekofoto/categories.py b/src/gnome/gnomekofoto/categories.py
new file mode 100644 (file)
index 0000000..0a550f0
--- /dev/null
@@ -0,0 +1,419 @@
+import gtk
+import gobject
+import gtk
+import string
+
+from environment import env
+from images import Images
+from categorydialog import CategoryDialog
+from kofoto.search import *
+from kofoto.shelf import *
+
+class Categories:
+    _COLUMN_CATEGORY_ID  = 0
+    _COLUMN_TAG          = 1
+    _COLUMN_DESCRIPTION  = 2
+    _COLUMN_CONNECTED    = 3
+    _COLUMN_INCONSISTENT = 4
+    _model = None
+    _toggleColumn = None
+    _ignoreSelectEvent = gtk.FALSE
+    _selectedCategories = {}
+    
+    def __init__(self, loadedImages, selectedImages, source):
+        self._model = gtk.TreeStore(gobject.TYPE_INT,      # CATEGORY_ID
+                                    gobject.TYPE_PYOBJECT, # TAG
+                                    gobject.TYPE_STRING,   # DESCRIPTION
+                                    gobject.TYPE_BOOLEAN,  # CONNECTED
+                                    gobject.TYPE_BOOLEAN)  # INCONSISTENT
+        categoryView = env.widgets["categoryView"]
+        categoryView.realize()
+        self._source = source
+        self._loadedImages = loadedImages
+        self._selectedImages = selectedImages
+        categoryView.set_model(self._model)
+
+        # Create columns
+        renderer = gtk.CellRendererToggle()
+        renderer.connect("toggled", self._connectionToggled)
+        self._toggleColumn = gtk.TreeViewColumn("",
+                                          renderer,
+                                          active=self._COLUMN_CONNECTED,
+                                          inconsistent=self._COLUMN_INCONSISTENT)
+        categoryView.append_column(self._toggleColumn)
+        
+        renderer = gtk.CellRendererText()
+        column = gtk.TreeViewColumn("Category", renderer, text=self._COLUMN_DESCRIPTION)
+        categoryView.append_column(column)
+        categoryView.set_expander_column(column)
+
+        # Create context menu
+        self._contextMenu = gtk.Menu()
+
+        self._copyItem = gtk.MenuItem("Copy")
+        self._copyItem.show()
+        self._copyItem.connect("activate", self._copyCategory, None)
+        self._contextMenu.append(self._copyItem)
+
+        self._cutItem = gtk.MenuItem("Cut")
+        self._cutItem.show()
+        self._cutItem.connect("activate", self._cutCategory, None)
+        self._contextMenu.append(self._cutItem)
+        
+        self._pasteItem = gtk.MenuItem("Paste")
+        self._pasteItem.show()
+        self._pasteItem.connect("activate", self._pasteCategory, None)
+        self._contextMenu.append(self._pasteItem)
+
+        self._createChildItem = gtk.MenuItem("Create child category")
+        self._createChildItem.show()
+        self._createChildItem.connect("activate", self._createChildCategory, None)
+        self._contextMenu.append(self._createChildItem)
+        
+        self._createRootItem = gtk.MenuItem("Create root category")
+        self._createRootItem.show()
+        self._createRootItem.connect("activate", self._createRootCategory, None)
+        self._contextMenu.append(self._createRootItem)
+        
+        self._deleteItem = gtk.MenuItem("Delete selected categories")
+        self._deleteItem.show()
+        self._deleteItem.connect("activate", self._deleteCategory, None)
+        self._contextMenu.append(self._deleteItem)
+
+        self._disconnectItem = gtk.MenuItem("Disconnect selected categories")
+        self._disconnectItem.show()
+        self._disconnectItem.connect("activate", self._disconnectCategory, None)
+        self._contextMenu.append(self._disconnectItem)
+
+        self._propertiesItem = gtk.MenuItem("Properties")
+        self._propertiesItem.show()
+        self._propertiesItem.connect("activate", self._editProperties, None)
+        self._contextMenu.append(self._propertiesItem)
+
+        self.updateContextMenu()
+
+        # Init selection functions
+        categorySelection = categoryView.get_selection()
+        categorySelection.set_mode(gtk.SELECTION_MULTIPLE)
+        categorySelection.set_select_function(self._selectionFunction, None)
+        categorySelection.connect("changed", self._selectionUpdated)
+
+        # Connect events
+        categoryView.connect("button_press_event", self._button_pressed)
+        categoryView.connect("button_release_event", self._button_released)
+        categoryView.connect("row-activated", self._rowActivated)
+                                            
+        # Connect search button
+        env.widgets["categorySearchButton"].connect('clicked', self._executeQuery)
+        
+        # Load data into model
+        self.reload()
+    
+    def reload(self):
+        self._model.clear()
+        env.shelf.flushCategoryCache()
+        for category in env.shelf.getRootCategories():
+            self._populateModel(None, category)
+
+    def _populateModel(self, parent, category):
+        iter = self._model.append(parent)
+        self._model.set_value(iter, self._COLUMN_CATEGORY_ID, category.getId())
+        self._model.set_value(iter, self._COLUMN_TAG, category.getTag())
+        self._model.set_value(iter, self._COLUMN_DESCRIPTION, category.getDescription())
+        self._model.set_value(iter, self._COLUMN_CONNECTED, gtk.FALSE)
+        self._model.set_value(iter, self._COLUMN_INCONSISTENT, gtk.FALSE)
+        for child in category.getChildren():
+            self._populateModel(iter, child)
+
+    def _executeQuery(self, *foo):
+        query = self._buildQuery()
+        if query:
+            self._source.set("query://" + query)
+            
+    def _buildQuery(self):
+        if env.widgets["categoriesOr"].get_active():
+            operator = " or "
+        else:
+            operator = " and "
+        query = ""
+        for categoryId in self._selectedCategories:
+            if query:
+                query += operator
+            category = env.shelf.getCategory(categoryId)
+            query += category.getTag()
+        return query
+        
+    def _selectionUpdated(self, selection):
+        self._selectedCategories = {}
+        self._parseCategoryTree(self._model, None, selection)
+        self.updateContextMenu()
+            
+    def _parseCategoryTree(self, categoryList, parent, selection):
+        for row in categoryList:
+            categoryId = row[self._COLUMN_CATEGORY_ID]
+            if selection.iter_is_selected(row.iter):
+                if categoryId in self._selectedCategories:
+                    self._selectedCategories[categoryId].append(parent)
+                else:
+                    self._selectedCategories[categoryId] = [parent]
+            self._parseCategoryTree(row.iterchildren(), categoryId, selection)
+            
+    def updateContextMenu(self):
+        if len(self._selectedCategories) == 0:
+            self._deleteItem.set_sensitive(gtk.FALSE)
+            self._createChildItem.set_sensitive(gtk.FALSE)
+            self._copyItem.set_sensitive(gtk.FALSE)
+            self._cutItem.set_sensitive(gtk.FALSE)
+            self._pasteItem.set_sensitive(gtk.FALSE)
+            self._disconnectItem.set_sensitive(gtk.FALSE)
+        else:
+            self._deleteItem.set_sensitive(gtk.TRUE)
+            self._createChildItem.set_sensitive(gtk.TRUE)
+            self._copyItem.set_sensitive(gtk.TRUE)
+            self._cutItem.set_sensitive(gtk.TRUE)
+            if env.controller.clipboardHasCategory():
+                self._pasteItem.set_sensitive(gtk.TRUE)
+            else:
+                self._pasteItem.set_sensitive(gtk.FALSE)
+            self._disconnectItem.set_sensitive(gtk.TRUE)
+        if len(self._selectedCategories) == 1:
+            self._propertiesItem.set_sensitive(gtk.TRUE)
+        else:
+            self._propertiesItem.set_sensitive(gtk.FALSE)
+            
+    def updateView(self, doNotAutoCollapse=gtk.FALSE):
+        nrSelectedImagesInCategory = {}
+        nrSelectedImages = 0
+        # find out which categories are connected, not connected or
+        # partitionally connected to selected images
+        for image in self._loadedImages.model:
+            imageId = image[Images.COLUMN_IMAGE_ID]
+            if imageId in self._selectedImages:
+                nrSelectedImages += 1
+                image = env.shelf.getImage(imageId)
+                for category in image.getCategories():
+                    categoryId = category.getId()
+                    try:
+                        nrSelectedImagesInCategory[categoryId] += 1
+                    except(KeyError):
+                        nrSelectedImagesInCategory[categoryId] = 1
+        pathsToExpand = self._updateViewHelper(self._model,
+                                           nrSelectedImagesInCategory,
+                                           nrSelectedImages,
+                                           doNotAutoCollapse)[0]
+        if env.widgets["autoExpand"].get_active():
+            pathsToExpand.reverse()
+            for path in pathsToExpand:
+                env.widgets["categoryView"].expand_row(path, gtk.FALSE)  
+            
+    def _updateViewHelper(self, categories, nrSelectedImagesInCategory, nrSelectedImages, doNotAutoCollapse):
+        pathsToExpand = []
+        expandParent = gtk.FALSE
+        for categoryRow in categories:
+            childPathsToExpand, expandThis = self._updateViewHelper(categoryRow.iterchildren(),
+                                                                nrSelectedImagesInCategory,
+                                                                nrSelectedImages,
+                                                                doNotAutoCollapse)
+            pathsToExpand.extend(childPathsToExpand)
+            if expandThis:
+                pathsToExpand.append(categoryRow.path)
+                expandParent = gtk.TRUE
+            if categoryRow[self._COLUMN_CATEGORY_ID] in self._selectedCategories:
+                # Dont auto collapse selected categories
+                expandParent = gtk.TRUE
+            categoryId = categoryRow[self._COLUMN_CATEGORY_ID]
+            if categoryId in nrSelectedImagesInCategory:
+                expandParent = gtk.TRUE
+                if nrSelectedImagesInCategory[categoryId] < nrSelectedImages:
+                    # Some of the selected images are connected to the category
+                    categoryRow[self._COLUMN_CONNECTED] = gtk.FALSE
+                    categoryRow[self._COLUMN_INCONSISTENT] = gtk.TRUE
+                else:
+                    # All of the selected images are connected to the category
+                    categoryRow[self._COLUMN_CONNECTED] = gtk.TRUE
+                    categoryRow[self._COLUMN_INCONSISTENT] = gtk.FALSE
+            else:
+                # None of the selected images are connected to the category
+                categoryRow[self._COLUMN_CONNECTED] = gtk.FALSE
+                categoryRow[self._COLUMN_INCONSISTENT] = gtk.FALSE
+            if (not expandThis) and (not doNotAutoCollapse) and env.widgets["autoCollapse"].get_active():
+                env.widgets["categoryView"].collapse_row(categoryRow.path)
+        return pathsToExpand, expandParent
+                
+    def _connectionToggled(self, renderer, path):
+        categoryRow = self._model[path]
+        category = env.shelf.getCategory(categoryRow[self._COLUMN_CATEGORY_ID])
+        if categoryRow[self._COLUMN_INCONSISTENT]:
+            for imageId in self._selectedImages:
+                try:
+                    env.shelf.getObject(imageId).addCategory(category)
+                except(CategoryPresentError):
+                    pass
+            categoryRow[self._COLUMN_INCONSISTENT] = gtk.FALSE
+            categoryRow[self._COLUMN_CONNECTED] = gtk.TRUE
+        elif categoryRow[self._COLUMN_CONNECTED]:
+            for imageId in self._selectedImages:
+                env.shelf.getObject(imageId).removeCategory(category)
+            categoryRow[self._COLUMN_CONNECTED] = gtk.FALSE
+        else:
+            for imageId in self._selectedImages:
+                env.shelf.getObject(imageId).addCategory(category)
+                categoryRow[self._COLUMN_CONNECTED] = gtk.TRUE
+
+    def _button_pressed(self, treeView, event):
+        if event.button == 3:
+            self._contextMenu.popup(None,None,None,event.button,event.time)
+            return gtk.TRUE
+        rec = env.widgets["categoryView"].get_cell_area(0, self._toggleColumn)
+        if event.x <= (rec.x + rec.width):
+            self._ignoreSelectEvent = gtk.TRUE
+        return gtk.FALSE
+
+    def _button_released(self, treeView, event):
+        self._ignoreSelectEvent = gtk.FALSE
+        return gtk.FALSE
+        
+    def _rowActivated(self, a, b, c):
+        print "not yet implemented!"
+
+    def _copyCategory(self, item, data):
+        cc = ClipboardCategories()
+        cc.type = cc.COPY
+        cc.categories = self._selectedCategories
+        env.controller.clipboardSet(cc)
+
+    def _cutCategory(self, item, data):
+        cc = ClipboardCategories()
+        cc.type = cc.CUT
+        cc.categories = self._selectedCategories
+        env.controller.clipboardSet(cc)
+
+    def _pasteCategory(self, item, data):
+        clipboardCategories = env.controller.clipboardPop()
+        try:
+            for (categoryId, parentIds) in clipboardCategories.categories.items():
+                for selectedCategoryId in self._selectedCategories:
+                    self._connectChild(selectedCategoryId, categoryId)
+                    self._expandCategory(selectedCategoryId, self._model)
+                if clipboardCategories.type == ClipboardCategories.CUT:
+                    for parentId in parentIds:
+                        if parentId != None:
+                            self._disconnectChild(parentId, categoryId)
+        except(CategoryLoopError):
+            print "Error: Category loop detected"
+            # TODO: Show dialog box with error message
+        self.updateView(doNotAutoCollapse = gtk.TRUE)
+
+    def _expandCategory(self, categoryId, categories):
+        for c in categories:
+            if c[self._COLUMN_CATEGORY_ID] == categoryId:
+                env.widgets["categoryView"].expand_row(c.path, gtk.FALSE)
+            self._expandCategory(categoryId, c.iterchildren())
+        
+    def _createRootCategory(self, item, data):
+        dialog = CategoryDialog("Create root category")
+        dialog.run(self._createRootCategoryHelper)
+       
+    def _createRootCategoryHelper(self, tag, desc):
+        category = env.shelf.createCategory(tag, desc)
+        self._populateModel(None, category)
+        self.updateView(doNotAutoCollapse = gtk.TRUE)
+
+    def _createChildCategory(self, item, data):
+        dialog = CategoryDialog("Create child category")
+        dialog.run(self._createChildCategoryHelper)
+
+    def _createChildCategoryHelper(self, tag, desc):
+        newCategory = env.shelf.createCategory(tag, desc)
+        for selectedCategoryId in self._selectedCategories:
+            self._connectChild(selectedCategoryId, newCategory.getId())
+        self.updateView(doNotAutoCollapse = gtk.TRUE)
+        
+    def _deleteCategory(self, item, data):
+        # TODO: Add confirmation dialog box
+        for categoryId in self._selectedCategories:
+            category = env.shelf.getCategory(categoryId)
+            for child in list(category.getChildren()):
+                # The backend automatically disconnects childs when a category
+                # is deleted, but we do it outself to make sure that the
+                # treeview widget is updated
+                self._disconnectChild(categoryId, child.getId())
+            env.shelf.deleteCategory(categoryId)
+            env.shelf.flushCategoryCache()
+            self._deleteCategoryHelper(self._model, categoryId)
+        self.updateView(doNotAutoCollapse = gtk.TRUE)
+
+    def _deleteCategoryHelper(self, categories, categoryId):
+         for c in categories:
+            if c[self._COLUMN_CATEGORY_ID] == categoryId:
+                self._model.remove(c.iter)
+            self._deleteCategoryHelper(c.iterchildren(), categoryId)
+
+    def _disconnectCategory(self, item, data):
+        for (categoryId, parentIds) in self._selectedCategories.items():
+            for parentId in parentIds:
+                if not parentId == None:
+                    self._disconnectChild(parentId, categoryId)
+        self.updateView(doNotAutoCollapse = gtk.TRUE)
+
+    def _editProperties(self, item, data):
+        for category in self._selectedCategories:
+            dialog = CategoryDialog("Change properties", category)
+            dialog.run(self._editPropertiesHelper, data=category)
+
+    def _editPropertiesHelper(self, tag, desc, categoryId):
+         category = env.shelf.getCategory(categoryId)
+         category.setTag(tag)
+         category.setDescription(desc)
+         self._updateProperties(categoryId, self._model)
+
+    def _updateProperties(self, categoryId, categories):
+        for row in categories:
+            if row[self._COLUMN_CATEGORY_ID] == categoryId:
+                c = env.shelf.getCategory(categoryId)
+                row[self._COLUMN_TAG] = c.getTag()
+                row[self._COLUMN_DESCRIPTION] = c.getDescription()
+            self._updateProperties(categoryId, row.iterchildren())
+        
+    def _selectionFunction(self, path, b):
+        if self._ignoreSelectEvent:
+            return gtk.FALSE
+        else:
+            return gtk.TRUE
+
+    def _connectChild(self, parentId, childId):
+        childCategory = env.shelf.getCategory(childId)
+        parentCategory = env.shelf.getCategory(parentId)
+        parentCategory.connectChild(childCategory)
+        self._connectChildHelper(self._model, parentId, childCategory)
+        for category in self._model:
+            if category[self._COLUMN_CATEGORY_ID] == childId:
+                self._model.remove(category.iter)
+        
+    def _connectChildHelper(self, categories, parentId, child):
+        for c in categories:
+            if c[self._COLUMN_CATEGORY_ID] == parentId:
+                self._populateModel(c.iter, child)
+            self._connectChildHelper(c.iterchildren(), parentId, child)
+
+    def _disconnectChild(self, parentId, childId):
+        childCategory = env.shelf.getCategory(childId)
+        parentCategory = env.shelf.getCategory(parentId)
+        parentCategory.disconnectChild(childCategory)
+        self._disconnectChildHelper(self._model, None, childId, parentId)
+        if len(list(childCategory.getParents())) == 0:
+            self._populateModel(None, childCategory)
+        
+    def _disconnectChildHelper(self, categories, parent, childId, parentId):
+        for c in categories:
+            id = c[self._COLUMN_CATEGORY_ID]
+            if id == childId and parent == parentId:
+                self._model.remove(c.iter)
+            self._disconnectChildHelper(c.iterchildren(), id, childId, parentId)
+
+class ClipboardCategories:
+    COPY = 1
+    CUT = 2
+    categories = None
+    type = None
+
diff --git a/src/gnome/gnomekofoto/categorydialog.py b/src/gnome/gnomekofoto/categorydialog.py
new file mode 100644 (file)
index 0000000..dcc5406
--- /dev/null
@@ -0,0 +1,64 @@
+import gtk
+import gobject
+import gtk
+import string
+
+from environment import env
+from kofoto.shelf import *
+
+class CategoryDialog:
+    def __init__(self, title, categoryId=None):
+        if categoryId:
+            category = env.shelf.getCategory(categoryId)
+            tagText = category.getTag()
+            descText = category.getDescription()
+        else:
+            tagText = ""
+            descText = ""
+            
+        widgets = gtk.glade.XML(env.gladeFile, "categoryProperties")
+        self._dialog = widgets.get_widget("categoryProperties")
+        self._dialog.set_title(title)
+        self._tagWidget = widgets.get_widget("tag")
+        self._tagWidget.set_text(tagText)
+        self._descWidget = widgets.get_widget("description")
+        self._descWidget.set_text(descText)
+        self._descWidget.connect("changed", self._descriptionChanged, self._tagWidget)
+        self._tagWidget.connect("changed", self._tagChanged, widgets.get_widget("okbutton"))
+
+    def run(self, ok=None, data=None):
+        result = self._dialog.run()
+        tag = self._tagWidget.get_text().decode("utf-8")
+        desc = self._descWidget.get_text().decode("utf-8")            
+        self._dialog.destroy()       
+        if result == gtk.RESPONSE_OK:
+            if ok == None:
+                return None
+            else:
+                if data:
+                    return ok(tag, desc, data)
+                else:
+                    return ok(tag, desc)
+        else:
+            return None
+    
+    def _descriptionChanged(self, description, tag):
+        # Remove all whitespaces in description and then use it as tag
+        tag.set_text(string.translate(description.get_text(),
+                                      string.maketrans("", ""),
+                                      string.whitespace))
+
+    def _tagChanged(self, tag, button):
+        tagString = tag.get_text().decode("utf-8")
+        try:
+           # Check that the tag name is valid
+           verifyValidCategoryTag(tagString)
+        except(BadCategoryTagError):
+            button.set_sensitive(gtk.FALSE)
+            return
+        try:
+            # Make sure that the tag name is not already taken
+            env.shelf.getCategory(tagString)
+            button.set_sensitive(gtk.FALSE)
+        except(CategoryDoesNotExistError):
+            button.set_sensitive(gtk.TRUE)
diff --git a/src/gnome/gnomekofoto/environment.py b/src/gnome/gnomekofoto/environment.py
new file mode 100644 (file)
index 0000000..d9fd2e9
--- /dev/null
@@ -0,0 +1,41 @@
+import sys
+import os
+import getopt
+import locale
+
+# Find libraries if installed in ../lib (like in the source tree).
+if os.path.islink(sys.argv[0]):
+    link = os.readlink(sys.argv[0])
+    absloc = os.path.normpath(
+        os.path.join(os.path.dirname(sys.argv[0]), link))
+    bindir = os.path.dirname(absloc)
+else:
+    bindir = os.path.dirname(sys.argv[0])
+sys.path.insert(0, os.path.join(bindir, "..", "lib"))
+
+from kofoto.common import *
+from kofoto.shelf import *
+from kofoto.config import *
+
+class Environment:
+     # TODO: read from configuration file?
+    defaultTableViewColumns = [u"title", u"description", u"captured"]
+    defaultThumbnailViewColumns = [u"captured"]
+    defaultSortColumn = u"captured"
+
+env = Environment()
+locale.setlocale(locale.LC_ALL, "")
+env.codeset = locale.nl_langinfo(locale.CODESET)
+conf = Config(DEFAULT_CONFIGFILE, env.codeset)
+conf.read()
+genconf = conf.getGeneralConfig()
+
+dataDir = os.path.join(bindir, "..", "share", "gnomekofoto")
+if not os.path.exists(dataDir):
+    dataDir = bindir
+
+env.imageCacheLocation = genconf["imagecache_location"]
+env.imageSizes = genconf["image_sizes"]
+env.iconDir = os.path.join(dataDir, "icons")
+env.gladeFile = os.path.join(dataDir, "glade", "gkofoto.glade")
+env.shelf = Shelf(genconf["shelf_location"], env.codeset)
diff --git a/src/gnome/gnomekofoto/imagecontextmenu.py b/src/gnome/gnomekofoto/imagecontextmenu.py
new file mode 100644 (file)
index 0000000..0694771
--- /dev/null
@@ -0,0 +1,91 @@
+import gtk
+import gobject
+import gtk
+
+from environment import env
+
+class ImageContextMenu(gtk.Menu):
+
+    def __init__(self, images, selectedImages):
+        gtk.Menu.__init__(self)
+
+        self._unregisterItem = gtk.MenuItem("Unregister")
+        self._unregisterItem.show()
+        self._unregisterItem.connect("activate", images.unregisterImages, None)
+        self.append(self._unregisterItem)
+
+        self._rotateLeftItem = gtk.MenuItem("Rotate left")
+        self._rotateLeftItem.show()
+        self._rotateLeftItem.connect("activate", images.rotate, 270)
+        self.append(self._rotateLeftItem)
+        
+        self._rotateRightItem = gtk.MenuItem("Rotate right")
+        self._rotateRightItem.show()
+        self._rotateRightItem.connect("activate", images.rotate, 90)
+        self.append(self._rotateRightItem)
+
+        self._tableViewGroup = None
+        self.tableViewSortItem = gtk.MenuItem("Sort by")
+        self._tableViewSortSubMenu = gtk.Menu()
+
+        sortAscendingItem = gtk.RadioMenuItem(None, "Ascending")
+        sortDescendingItem = gtk.RadioMenuItem(sortAscendingItem, "Descending")
+        sortAscendingItem.connect("activate", images.setSortOrder, gtk.SORT_ASCENDING)
+        sortDescendingItem.connect("activate", images.setSortOrder, gtk.SORT_DESCENDING)
+        sortAscendingItem.activate()
+        sortSeparator = gtk.SeparatorMenuItem()
+        sortAscendingItem.show()
+        sortDescendingItem.show()
+        sortSeparator.show()
+        self._tableViewSortSubMenu.append(sortAscendingItem)
+        self._tableViewSortSubMenu.append(sortDescendingItem)
+        self._tableViewSortSubMenu.append(sortSeparator)
+        self.tableViewSortItem.set_submenu(self._tableViewSortSubMenu)
+        self.tableViewSortItem.show()
+        self.append(self.tableViewSortItem)
+
+        
+        self.tableViewViewItem = gtk.MenuItem("View")
+        self._tableViewViewSubMenu = gtk.Menu()
+        self.tableViewViewItem.set_submenu(self._tableViewViewSubMenu)
+        self.tableViewViewItem.show()
+        self.append(self.tableViewViewItem)
+
+        self._images = images
+        self._selectedImages = selectedImages
+        self.updateContextMenu()
+
+        
+    def addTableViewColumn(self, name, modelColumn, widget):
+        # Populate the sort menu
+        sortItem = gtk.RadioMenuItem(self._tableViewGroup, name)
+        sortItem.connect("activate", self._images.sortByColumn, modelColumn)
+        if self._tableViewGroup == None:
+            self._tableViewGroup = sortItem
+        sortItem.show()
+        self._tableViewSortSubMenu.append(sortItem)
+        if name == env.defaultSortColumn:
+            sortItem.activate()
+        # Populate the view menu
+        viewItem = gtk.CheckMenuItem(name)
+        viewItem.connect("toggled", self._columnToggled, widget)
+        viewItem.show()
+        self._tableViewViewSubMenu.append(viewItem)
+        if name in env.defaultTableViewColumns:
+            viewItem.set_active(gtk.TRUE)
+        else:
+            viewItem.set_active(gtk.FALSE)
+        self._columnToggled(viewItem, widget)
+
+    def _columnToggled(self, checkMenuItem, widget):
+        widget.set_visible(checkMenuItem.get_active())
+        
+    def updateContextMenu(self):
+        if len(self._selectedImages) == 0:
+            self._unregisterItem.set_sensitive(gtk.FALSE)
+            self._rotateLeftItem.set_sensitive(gtk.FALSE)
+            self._rotateRightItem.set_sensitive(gtk.FALSE)
+        else:
+            self._unregisterItem.set_sensitive(gtk.TRUE)
+            self._rotateLeftItem.set_sensitive(gtk.TRUE)
+            self._rotateRightItem.set_sensitive(gtk.TRUE)
diff --git a/src/gnome/gnomekofoto/images.py b/src/gnome/gnomekofoto/images.py
new file mode 100644 (file)
index 0000000..e77d820
--- /dev/null
@@ -0,0 +1,148 @@
+import os
+import gtk
+import gobject
+import gc
+from sets import *
+from kofoto.imagecache import *
+from kofoto.shelf import *
+
+from environment import env
+from mysortedmodel import *
+
+class Images:
+    _unsortedModel = None
+    model = None
+    _title = ""
+    _imageCache = None
+    _sortColumn = None
+    
+    attributeNamesMap = {}
+
+    COLUMN_IMAGE_ID = 0
+    COLUMN_LOCATION = 1
+    COLUMN_THUMBNAIL = 2
+    COLUMN_VALID_LOCATION = 3
+    COLUMN_VALID_CHECKSUM = 4
+    COLUMN_ROW_EDITABLE   = 5
+    
+    _MANDATORY_COLUMNS_TYPE = [gobject.TYPE_INT,      # COLUMN_IMAGE_ID
+                               gobject.TYPE_STRING,   # COLUMN_LOCATION
+                               gtk.gdk.Pixbuf,        # COLUMN_THUMBNAIL
+                               gobject.TYPE_BOOLEAN,  # COLUMN_VALID_LOCATION
+                               gobject.TYPE_BOOLEAN,  # COLUMN_VALID_CHECKSUM
+                               gobject.TYPE_BOOLEAN]  # COLUMN_ROW_EDITABLE
+    
+
+    def __init__(self, selectedImages):
+        self._selectedImages = selectedImages
+        self._imageCache = ImageCache(env.imageCacheLocation)
+        self._loadModel()
+
+    def reloadModel(self):
+        self._loadModel()
+        env.controller.newImageModelLoaded()
+
+    def _loadModel(self):
+        columnsType = self._MANDATORY_COLUMNS_TYPE
+        allAttributeNames = Set(env.shelf.getAllAttributeNames())
+        allAttributeNames = allAttributeNames | Set(env.defaultTableViewColumns)
+        allAttributeNames = allAttributeNames | Set(env.defaultThumbnailViewColumns)
+        allAttributeNames = allAttributeNames | Set([env.defaultSortColumn])
+        for attributeName in allAttributeNames:
+            self.attributeNamesMap[attributeName] = len(columnsType)
+            columnsType.append(gobject.TYPE_STRING)
+        self._unsortedModel = gtk.ListStore(*columnsType)
+        self.model = MySortedModel(self._unsortedModel)
+
+    def loadImageList(self, imageList):
+        self._unsortedModel.clear()
+        gc.collect()
+        self._thumbnailSize = 0
+        for image in imageList:
+            iter = self._unsortedModel.append()
+            self._unsortedModel.set_value(iter, self.COLUMN_IMAGE_ID, image.getId()) 
+            self._unsortedModel.set_value(iter, self.COLUMN_LOCATION, image.getLocation())
+            self._unsortedModel.set_value(iter, self.COLUMN_ROW_EDITABLE, gtk.TRUE)
+            for attribute, value in image.getAttributeMap().items():
+                self._unsortedModel.set_value(iter, self.attributeNamesMap[attribute], value)
+                # TODO: update COLUMN_VALID_LOCATION
+
+    def loadThumbnails(self, wantedThumbnailSize):
+        iter = self._unsortedModel.get_iter_first()
+        while iter:
+            self._loadThumbnail(wantedThumbnailSize, iter)
+            iter = self._unsortedModel.iter_next(iter)
+        self._thumbnailSize = wantedThumbnailSize
+
+    def _loadThumbnail(self, wantedThumbnailSize, iter, reload=gtk.FALSE):
+        # Reload was used when thumbnail has been invalid and can not be reused.
+        # It is probably better to remove the "reload" parameter and instead
+        # remove thumbnails from the not-yet-existing-cache when needed.
+        try:
+            imageId = self._unsortedModel.get_value(iter, self.COLUMN_IMAGE_ID)
+            image = env.shelf.getImage(imageId)
+            thumbnailLocation = self._imageCache.get(image, wantedThumbnailSize)
+            pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnailLocation)
+            self._unsortedModel.set_value(iter, self.COLUMN_THUMBNAIL, pixbuf)
+        except IOError:
+            # TODO: Show some kind of error icon?
+            print "IOError"
+
+    def scalePixBuf(self, pixbuf, maxWidth, maxHeight):
+        scale = min(float(maxWidth) / pixbuf.get_width(), float(maxHeight) / pixbuf.get_height())
+        scale = min(1, scale)
+        if scale == 1:
+            return pixbuf
+        else:
+            return pixbuf.scale_simple(pixbuf.get_width() * scale,
+                                       pixbuf.get_height() * scale,
+                                       gtk.gdk.INTERP_BILINEAR) # gtk.gdk.INTERP_HYPER is slower but gives better quality.
+
+    
+    def unregisterImages(self, *foo):
+        # TODO Show dialog and ask for confirmation!
+        imageIdList = list(self._selectedImages)
+        self._selectedImages.clear()
+        for row in self._unsortedModel:
+            if row[self.COLUMN_IMAGE_ID] in imageIdList:
+                env.shelf.deleteImage(row[self.COLUMN_IMAGE_ID])
+                self._unsortedModel.remove(row.iter)
+
+    def rotate(self, button, angle):
+        # TODO: Make it possible for the user to configure if a rotation
+        # shall rotate the image or only update the orientation attribute?
+        for row in self._unsortedModel:
+            if row[self.COLUMN_IMAGE_ID] in self._selectedImages:
+                image = env.shelf.getImage(row[self.COLUMN_IMAGE_ID])
+                location = image.getLocation().encode(env.codeset)
+                # TODO: Read command from configuration file?
+                command = "jpegtran -rotate %s -perfect -copy all -outfile %s %s" % (angle, location, location)
+                result = os.system(command)
+                if result == 0:
+                    newHash = computeImageHash(location)
+                    image.setHash(newHash)
+                else:
+                    print "failed to execute:", command
+                self._loadThumbnail(100, row.iter, reload=gtk.TRUE)
+
+    def setSortOrder(self, widget, order):
+        self._sortOrder = order
+        if self._sortColumn:
+            self.sortByColumn(None, self._sortColumn)
+
+    def sortByColumn(self, menuItem, column):
+        self.model.set_sort_column_id(column, self._sortOrder)
+        self.model.set_sort_func(column, self._sort_func, column)
+        self._sortColumn = column
+
+    def _sort_func(self, model, iterA, iterB, column):
+        valueA = model.get_value(iterA, column)
+        valueB = model.get_value(iterB, column)
+        try:
+            result = cmp(float(valueA), float(valueB))
+        except (ValueError, TypeError):
+            result = cmp(valueA, valueB)
+        if result == 0:
+            result = cmp(model.get_value(iterA, self.COLUMN_IMAGE_ID),
+                         model.get_value(iterB, self.COLUMN_IMAGE_ID))
+        return result
diff --git a/src/gnome/gnomekofoto/imageselection.py b/src/gnome/gnomekofoto/imageselection.py
new file mode 100644 (file)
index 0000000..7718624
--- /dev/null
@@ -0,0 +1,35 @@
+from environment import env
+from kofoto.sets import *
+
+class ImageSelection:
+    def __init__(self, changedCallback):
+        self._set = Set()
+        self._changedCallback = changedCallback
+
+    def clear(self):
+        self._set.clear()
+        self._changedCallback()
+
+    def set(self, imageIdList):
+        self._set.clear()
+        for imageId in imageIdList:
+            self._set.add(imageId)
+        self._changedCallback()
+
+    def add(self, imageId):
+        self._set.add(imageId)
+        self._changedCallback()
+
+    def remove(self, imageId):
+        self._set.remove(imageId)
+        self._changedCallback()
+
+    def __contains__(self, imageId):
+        return imageId in self._set
+
+    def __len__(self):
+        return len(self._set)
+
+    def __iter__(self):
+        return self._set.__iter__()
+        
diff --git a/src/gnome/gnomekofoto/imageview.py b/src/gnome/gnomekofoto/imageview.py
new file mode 100644 (file)
index 0000000..5b408e1
--- /dev/null
@@ -0,0 +1,136 @@
+import gtk
+import gtk.gdk
+import math
+import gobject
+import gc
+from gtk import TRUE, FALSE
+from environment import env
+
+class ImageView(gtk.ScrolledWindow):
+    # TODO: Read from configuration file?
+    _INTERPOLATION_TYPE = gtk.gdk.INTERP_BILINEAR
+    # gtk.gdk.INTERP_HYPER is slower but gives better quality.
+    _MAX_IMAGE_SIZE = 2000
+    _MIN_IMAGE_SIZE = 1
+    _MIN_ZOOM = -100
+    _MAX_ZOOM = 1
+    _ZOOMFACTOR = 1.2
+    
+    _pixBuf = None
+    _currentZoom = None
+    _wantedZoom = None
+    _image = gtk.Image()            
+    _fitToWindowMode = TRUE
+    _previousWidgetWidth = 0
+    _previousWidgetHeight = 0
+    
+    def __init__(self):
+        gtk.ScrolledWindow.__init__(self)
+        eventBox = gtk.EventBox()
+        eventBox.add(self._image)
+        self.add_with_viewport(eventBox)
+        self.add_events(gtk.gdk.ALL_EVENTS_MASK)
+        self.connect_after("size_allocate", self.resizeEventHandler)
+        self.connect("scroll_event", self.scrollEventHandler)
+
+    def loadFile(self, filename):
+        # TODO: Loading file should be asyncronous to avoid freezing the gtk-main loop
+        try:
+            gc.collect()
+            self._pixBuf = gtk.gdk.pixbuf_new_from_file(filename.encode(env.codeset))
+            self._image.show()
+            self._newImageLoaded = TRUE
+            self.fitToWindow()
+        except gobject.GError:
+            print "GError while loading ", filename
+            self._pixBuf = None
+            self._image.hide()
+
+    def clear(self):
+        self._image.hide()
+        self._image.set_from_file(None)
+        self._pixBuf = None
+        gc.collect()
+            
+    def renderImage(self):
+        # TODO: Scaling should be asyncronous to avoid freezing the gtk-main loop
+        if self._pixBuf == None:
+            # No image loaded
+            self._image.hide()
+            return
+        if self._currentZoom == self._wantedZoom and self._newImageLoaded == FALSE:
+            return
+        if self._wantedZoom == 0:
+            pixBufResized = self._pixBuf
+        else:
+            zoomMultiplicator = pow(self._ZOOMFACTOR, self._wantedZoom)
+            wantedWidth = int(self._pixBuf.get_width() * zoomMultiplicator)
+            wantedHeight = int(self._pixBuf.get_height() * zoomMultiplicator)
+            if min(wantedWidth, wantedHeight) < self._MIN_IMAGE_SIZE:
+                # Too small image size
+                return
+            if max(wantedWidth, wantedHeight) > self._MAX_IMAGE_SIZE:
+                # Too large image size
+                return
+            pixBufResized = self._pixBuf.scale_simple(wantedWidth,
+                                                      wantedHeight,
+                                                      self._INTERPOLATION_TYPE)
+        pixMap, mask = pixBufResized.render_pixmap_and_mask()
+        self._image.set_from_pixmap(pixMap, mask)
+        self._newImageLoaded = FALSE
+        self._currentZoom = self._wantedZoom
+        gc.collect()
+
+    def resizeEventHandler(self, widget, gdkEvent):
+        if self._fitToWindowMode == TRUE:
+            x, y, width, height = self.get_allocation()
+            if height != self._previousWidgetHeight or width != self._previousWidgetWidth:
+                self.fitToWindow()
+        return FALSE
+        
+    def fitToWindow(self, *foo):
+        self._fitToWindowMode = TRUE
+        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
+        y, x, widgetWidth, widgetHeight = self.get_allocation()
+        if self._pixBuf != None:
+            self._previousWidgetWidth = widgetWidth
+            self._previousWidgetHeight = widgetHeight
+            a = min(float(widgetWidth) / self._pixBuf.get_width(),
+                    float(widgetHeight) / self._pixBuf.get_height())
+            self._wantedZoom = self._log(self._ZOOMFACTOR, a)
+            self._wantedZoom = min(self._wantedZoom, 0)
+            self._wantedZoom = max(self._wantedZoom, self._MIN_ZOOM)
+            self.renderImage()
+        
+    def _log(self, base, value):
+        return math.log(value) / math.log(base)
+
+    def zoomIn(self, *foo):
+        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self._fitToWindowMode = FALSE
+        if self._wantedZoom <= self._MAX_ZOOM:
+            self._wantedZoom = math.floor(self._wantedZoom + 1)
+            self.renderImage()
+                
+    def zoomOut(self, *foo):
+        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self._fitToWindowMode = FALSE
+        if self._wantedZoom >= self._MIN_ZOOM:
+            self._wantedZoom = math.ceil(self._wantedZoom - 1)
+            self.renderImage()
+
+    def zoom100(self, *foo):
+        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self._fitToWindowMode = FALSE
+        self._wantedZoom = 0
+        self.renderImage()
+                    
+    def scrollEventHandler(self, widget, gdkEvent):
+        if gdkEvent.type == gtk.gdk.SCROLL:
+            if gdkEvent.direction == gtk.gdk.SCROLL_UP:
+                self.zoomOut()
+            elif gdkEvent.direction == gtk.gdk.SCROLL_DOWN:
+                self.zoomIn()
+            return TRUE
+        else:
+            return FALSE
diff --git a/src/gnome/gnomekofoto/mainwindow.py b/src/gnome/gnomekofoto/mainwindow.py
new file mode 100644 (file)
index 0000000..e52fbe0
--- /dev/null
@@ -0,0 +1,70 @@
+import gtk
+import gtk.gdk
+import os
+
+from environment import env
+
+class MainWindow(gtk.Window):
+    def __init__(self):
+        self._toggleLock = gtk.FALSE
+        
+        env.widgets["expandViewToggleButton"].connect("toggled", self._toggleExpandView)
+        env.widgets["expandViewToggleButton"].get_child().add(self.getIconImage("fullscreen-24.png"))
+        env.widgets["attributeToggleButton"].connect("toggled", self._toggleAttributesView)
+        env.widgets["thumbnailsViewToggleButton"].connect("clicked", self._toggleThumbnailsView)
+        env.widgets["imageViewToggleButton"].connect("clicked", self._toggleImageView)
+        env.widgets["tableViewToggleButton"].connect("clicked", self._toggleTableView)
+        env.widgets["save"].connect("activate", env.controller.save)
+        env.widgets["quit"].connect("activate", env.controller.quit)
+
+        # Gray out not yet implemented stuff...
+        env.widgets["revert"].set_sensitive(gtk.FALSE)
+        env.widgets["copy"].set_sensitive(gtk.FALSE)
+        env.widgets["paste"].set_sensitive(gtk.FALSE)
+        env.widgets["preferences"].set_sensitive(gtk.FALSE)
+        
+    def getIconImage(self, name):
+        pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(env.iconDir, name))
+        image = gtk.Image()
+        image.set_from_pixbuf(pixbuf)
+        image.show()
+        return image
+
+    def _toggleExpandView(self, button):
+        if button.get_active():
+            env.widgets["sourceNotebook"].hide()
+        else:
+            env.widgets["sourceNotebook"].show()
+
+    def _toggleAttributesView(self, button):
+        if button.get_active():
+            env.widgets["attributeView"].show()
+        else:
+            env.widgets["attributeView"].hide()
+
+    def _toggleThumbnailsView(self, button):
+        if not self._toggleLock:
+            self._toggleLock = gtk.TRUE
+            button.set_active(gtk.TRUE)
+            env.widgets["imageViewToggleButton"].set_active(gtk.FALSE)
+            env.widgets["tableViewToggleButton"].set_active(gtk.FALSE)
+            env.controller.showThumbnailView()
+            self._toggleLock = gtk.FALSE
+
+    def _toggleImageView(self, button):
+        if not self._toggleLock:
+            self._toggleLock = gtk.TRUE
+            button.set_active(gtk.TRUE)
+            env.widgets["tableViewToggleButton"].set_active(gtk.FALSE)
+            env.widgets["thumbnailsViewToggleButton"].set_active(gtk.FALSE)
+            env.controller.showSingleImageView()
+            self._toggleLock = gtk.FALSE
+            
+    def _toggleTableView(self, button):
+        if not self._toggleLock:
+            self._toggleLock = gtk.TRUE
+            button.set_active(gtk.TRUE)
+            env.widgets["thumbnailsViewToggleButton"].set_active(gtk.FALSE)
+            env.widgets["imageViewToggleButton"].set_active(gtk.FALSE)
+            env.controller.showTableView()
+            self._toggleLock = gtk.FALSE
diff --git a/src/gnome/gnomekofoto/mysortedmodel.py b/src/gnome/gnomekofoto/mysortedmodel.py
new file mode 100644 (file)
index 0000000..a7c7776
--- /dev/null
@@ -0,0 +1,33 @@
+import gtk
+
+class MySortedModel(gtk.TreeModelSort):
+
+    def __init__(self, model):
+        gtk.TreeModelSort.__init__(self, model)
+        self._model = model
+    
+    def __getitem__(self, path):
+        child_path = self.convert_path_to_child_path(path)
+        if child_path:
+            return self._model[child_path]
+        else:
+            raise IndexError
+
+    def set_value(self, iter, column, value):
+        childIter = self._model.get_iter_first()
+        self.convert_iter_to_child_iter(childIter, iter)
+        self._model.set_value(childIter, column, value)
+
+    # Workaround until http://bugzilla.gnome.org/show_bug.cgi?id=121633 is solved.
+    def get_iter_first(self):
+        if len(self) > 0:
+            return gtk.TreeModelSort.get_iter_first(self)
+        else:
+            return None
+
+    # Workaround until http://bugzilla.gnome.org/show_bug.cgi?id=121633 is solved.       
+    def __iter__(self):
+        if len(self._model) > 0:
+            return gtk.TreeModelSort.__iter__(self)
+        else:
+            return self._model.__iter__()
diff --git a/src/gnome/gnomekofoto/singleimageview.py b/src/gnome/gnomekofoto/singleimageview.py
new file mode 100644 (file)
index 0000000..1a76d34
--- /dev/null
@@ -0,0 +1,132 @@
+# -*- coding: iso-8859-1 -*-
+
+import gtk
+import sys
+
+from environment import env
+from images import *
+from imageview import *
+
+# TODO: Prenumerera på model changes för att veta när tex en bild roterats
+
+class SingleImageView(ImageView):
+    _modelConnections = []
+    
+    def __init__(self, loadedImages, selectedImages, contextMenu):
+        ImageView.__init__(self)
+        self._locked = gtk.FALSE
+        self._freezed = gtk.FALSE
+        env.widgets["imageView"].add(self)
+        self.show_all()
+        self._selectedImages = selectedImages
+        self._contextMenu = contextMenu
+        env.widgets["nextButton"].connect("clicked", self._goto, 1)
+        env.widgets["previousButton"].connect("clicked", self._goto, -1)
+        env.widgets["zoomToFit"].connect("clicked", self.fitToWindow)
+        env.widgets["zoom100"].connect("clicked", self.zoom100)
+        env.widgets["zoomIn"].connect("clicked", self.zoomIn)
+        env.widgets["zoomOut"].connect("clicked", self.zoomOut)
+        self.connect("button_press_event", self._button_pressed)
+        self.setModel(loadedImages)
+    
+    def setModel(self, loadedImages):
+        for c in self._modelConnections:
+            self._model.disconnect(c)
+        del self._modelConnections[:]
+        self._model = loadedImages.model
+        c = self._model.connect("row_inserted", self._modelRowsChanged)
+        self._modelConnections.append(c)
+        c = self._model.connect("row_changed", self.selectionUpdated)
+        self._modelConnections.append(c)
+        c = self._model.connect("row_deleted", self._modelRowsChanged)
+        self._modelConnections.append(c)
+        self._modelRowsChanged()
+
+    def _modelRowsChanged(self, *foo):
+        if len(self._selectedImages) != 0:
+            self._selectedImages.clear()
+        self.selectionUpdated()
+        
+    def selectionUpdated(self, *foo):
+        if not self._freezed and not self._locked:
+            self._locked = gtk.TRUE
+            try:
+                if len(self._selectedImages) == 0:
+                    imageId = self._model[0][Images.COLUMN_IMAGE_ID]
+                    self._selectedImages.set([imageId])
+                else:
+                    imageId = list(self._selectedImages)[0]
+                    self._selectedImages.set([imageId])
+                self._imageId = imageId
+                self._updateButtons(self._getRow())
+                self._loadImage()
+            except(IndexError):
+                self.clear()
+                env.widgets["previousButton"].set_sensitive(gtk.FALSE)
+                env.widgets["nextButton"].set_sensitive(gtk.FALSE)
+            self._locked = gtk.FALSE
+
+    def _goto(self, button, direction):
+        if not self._locked:
+            self._locked = gtk.TRUE        
+            row = self._getRow() + direction
+            self._updateButtons(row)
+            self._imageId = self._model[row][Images.COLUMN_IMAGE_ID]
+            self._loadImage()
+            self._selectedImages.set([self._imageId])
+            self._locked = gtk.FALSE
+
+    def _getRow(self):
+        for image in self._model:
+            if image[Images.COLUMN_IMAGE_ID] == self._imageId:
+                return image.path[0]
+        raise IndexError
+
+    def _updateButtons(self, row):
+        if row <= 0:
+            env.widgets["previousButton"].set_sensitive(gtk.FALSE)
+        else: 
+            env.widgets["previousButton"].set_sensitive(gtk.TRUE)
+        if row >= len(self._model) - 1:
+            env.widgets["nextButton"].set_sensitive(gtk.FALSE)
+        else:
+            env.widgets["nextButton"].set_sensitive(gtk.TRUE)
+            
+    def _loadImage(self):
+        image = env.shelf.getImage(self._imageId)
+        self.loadFile(image.getLocation())
+        
+    def freeze(self):
+        self._freezed = gtk.TRUE
+
+    def thaw(self):
+        self._freezed = gtk.FALSE
+        self.selectionUpdated()
+
+    def show(self):
+        self.thaw()
+        env.widgets["imageView"].show()
+        env.widgets["imageView"].grab_focus()
+        env.widgets["zoom100"].set_sensitive(gtk.TRUE)
+        env.widgets["zoomToFit"].set_sensitive(gtk.TRUE)
+        env.widgets["zoomIn"].set_sensitive(gtk.TRUE)
+        env.widgets["zoomOut"].set_sensitive(gtk.TRUE)
+        self.thaw()
+
+    def hide(self):
+        self.freeze()
+        self.clear()
+        env.widgets["imageView"].hide()
+        env.widgets["previousButton"].set_sensitive(gtk.FALSE)
+        env.widgets["nextButton"].set_sensitive(gtk.FALSE)
+        env.widgets["zoom100"].set_sensitive(gtk.FALSE)
+        env.widgets["zoomToFit"].set_sensitive(gtk.FALSE)
+        env.widgets["zoomIn"].set_sensitive(gtk.FALSE)
+        env.widgets["zoomOut"].set_sensitive(gtk.FALSE)
+        self.freeze()
+
+    def _button_pressed(self, widget, event):
+        if event.button == 3:
+            if self._contextMenu:
+                self._contextMenu.popup(None,None,None,event.button,event.time)
+            return gtk.TRUE
diff --git a/src/gnome/gnomekofoto/source.py b/src/gnome/gnomekofoto/source.py
new file mode 100644 (file)
index 0000000..e62c0bd
--- /dev/null
@@ -0,0 +1,55 @@
+import string
+import gtk
+import gtk.gdk
+
+from environment import env
+from kofoto.search import *
+from kofoto.shelf import *
+
+class Source:
+    def __init__(self):
+        self._hasChanged = gtk.FALSE
+        # Using "focus-out-event" as a workaround, since there is no
+        # "editing_done" signal
+        env.widgets["sourceEntry"].connect("focus-out-event", self._updated)
+        env.widgets["sourceEntry"].connect("changed", self._changed)
+        
+    def _changed(self, *foo):
+        self._hasChanged = gtk.TRUE
+        
+    def _updated(self, widget=None, *foo):
+        if not self._hasChanged:
+            return
+        if widget==None:
+            widget = env.widgets["sourceEntry"]
+        source = widget.get_text()
+        l = string.split(source, u"://", 1)
+        imageList = []
+        if l[0] == u"query":
+            parser = Parser(env.shelf)
+            try:
+                for child in env.shelf.search(parser.parse(l[1])):
+                    if not child.isAlbum():
+                        imageList.append(child)
+            except(CategoryDoesNotExistError):
+                print "Unkown categories: ", l[1]
+                imageList = []
+            except(ParseError):
+                print "Invalid query: ", l[1]
+                imageList = []
+        elif l[0] == u"album":
+            try:
+                album = env.shelf.getAlbum(l[1])
+                for child in album.getChildren():
+                    if not child.isAlbum():
+                        imageList.append(child)
+            except(AlbumDoesNotExistError):
+                print "Unknown album: ", l[1]
+        else:
+            print "Unknown protocoll"
+        env.controller.loadImages(imageList)
+        self._hasChanged = gtk.FALSE
+                
+    def set(self, source):
+        env.widgets["sourceEntry"].set_text(source)        
+        self._updated()
diff --git a/src/gnome/gnomekofoto/tableview.py b/src/gnome/gnomekofoto/tableview.py
new file mode 100644 (file)
index 0000000..88db47f
--- /dev/null
@@ -0,0 +1,136 @@
+import gtk
+
+from environment import env
+from images import *
+from kofoto.sets import *
+
+class TableView:
+
+    def __init__(self, loadedImages, selectedImages, contextMenu):
+        self._locked = gtk.FALSE
+        self._tableView = env.widgets["tableView"]
+        selection = self._tableView.get_selection()
+        selection.set_mode(gtk.SELECTION_MULTIPLE)
+        self._contextMenu = contextMenu
+        self._selectedImages = selectedImages
+        self._tableView.connect("button_press_event", self._button_pressed)
+        selection.connect('changed', self._widgetSelectionUpdated)
+        self.setModel(loadedImages)
+
+    def setModel(self, loadedImages):
+        self._model = loadedImages.model
+        self._tableView.set_model(self._model)
+        for column in self._tableView.get_columns():
+            self._tableView.remove_column(column)
+        self._createThumbnailColumn()
+        self._createIdColumn()
+        self._createLocationColumn()
+        self._createAttributeColumns(loadedImages.attributeNamesMap)
+
+    def _createThumbnailColumn(self):
+        renderer = gtk.CellRendererPixbuf()
+        column = gtk.TreeViewColumn("Thumbnail", renderer, pixbuf=Images.COLUMN_THUMBNAIL)
+        column.set_reorderable(gtk.TRUE)
+        self._tableView.append_column(column)
+
+    def _createIdColumn(self):
+        renderer = gtk.CellRendererText()
+        columnName = u"ImageId"
+        column = gtk.TreeViewColumn(columnName, renderer, text=Images.COLUMN_IMAGE_ID)
+        column.set_resizable(gtk.TRUE)
+        column.set_reorderable(gtk.TRUE)
+        self._contextMenu.addTableViewColumn(columnName, Images.COLUMN_IMAGE_ID, column)
+        self._tableView.append_column(column)
+
+    def _createLocationColumn(self):
+        renderer = gtk.CellRendererText()
+        columnName = u"Location"
+        column = gtk.TreeViewColumn(columnName, renderer,
+                                    text=Images.COLUMN_LOCATION)
+        column.set_resizable(gtk.TRUE)
+        column.set_reorderable(gtk.TRUE)
+        self._contextMenu.addTableViewColumn(columnName, Images.COLUMN_LOCATION, column)
+        self._tableView.append_column(column)        
+
+    def _createAttributeColumns(self, attributeNamesMap):
+        allAttributeNames = attributeNamesMap.keys()
+        allAttributeNames.sort()
+        for attributeName in allAttributeNames:
+            columnNumber = attributeNamesMap[attributeName]
+            renderer = gtk.CellRendererText()
+            column = gtk.TreeViewColumn(attributeName,
+                                        renderer,
+                                        text=columnNumber,
+                                        editable=Images.COLUMN_ROW_EDITABLE)
+            column.set_resizable(gtk.TRUE)
+            column.set_reorderable(gtk.TRUE)
+            renderer.connect("edited", self._attribute_editing_done,
+                             columnNumber,
+                             attributeName)
+            self._contextMenu.addTableViewColumn(attributeName, columnNumber, column)
+            self._tableView.append_column(column)
+
+    def _attribute_editing_done(self, renderer, path, value, column, attributeName):
+        iter = self._model.get_iter(path)
+        oldValue = self._model.get_value(iter, column)
+        if not oldValue:
+            oldValue = u""
+        value = unicode(value, "utf-8")
+        if oldValue != value:
+            # TODO Show dialog and ask for confirmation?
+            imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
+            image = env.shelf.getImage(imageId)
+            image.setAttribute(attributeName, value)
+            self._model.set_value(iter, column, value)
+            
+    def freeze(self):
+        pass
+
+    def thaw(self):
+        pass
+
+    def show(self):
+        env.widgets["tableViewScroll"].show()
+        self._contextMenu.tableViewViewItem.show()
+        self._tableView.grab_focus()
+        for image in self._model:
+            if image[Images.COLUMN_IMAGE_ID] in self._selectedImages:
+                self._tableView.scroll_to_cell(image.path, None, gtk.TRUE, 0, 0)
+                break
+
+    def hide(self):
+        env.widgets["tableViewScroll"].hide()
+        self._contextMenu.tableViewViewItem.hide()
+        
+    def selectionUpdated(self):
+        if not self._locked:
+            self._locked = gtk.TRUE
+            selection = self._tableView.get_selection()
+            selection.unselect_all()
+            iter = self._model.get_iter_first()
+            index = 0
+            while iter:
+                imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
+                if imageId in self._selectedImages:
+                    selection.select_path(index)
+                index = index + 1
+                iter =  self._model.iter_next(iter)
+            self._locked = gtk.FALSE
+
+    def _widgetSelectionUpdated(self, selection):
+        if not self._locked:
+            self._locked = gtk.TRUE
+            iter = self._model.get_iter_first()
+            imageIdList = []
+            while iter:
+                if selection.iter_is_selected(iter):
+                    imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
+                    imageIdList.append(imageId)
+                iter = self._model.iter_next(iter)
+            self._selectedImages.set(imageIdList)
+            self._locked = gtk.FALSE
+        
+    def _button_pressed(self, widget, event):
+        if event.button == 3:
+            self._contextMenu.popup(None,None,None,event.button,event.time)
+            return gtk.TRUE
diff --git a/src/gnome/gnomekofoto/thumbnailview.py b/src/gnome/gnomekofoto/thumbnailview.py
new file mode 100644 (file)
index 0000000..b3b501b
--- /dev/null
@@ -0,0 +1,131 @@
+import gtk
+import sys
+
+from environment import env
+from images import *
+
+class ThumbnailView:
+    _maxWidth = 0
+    _modelConnections = []
+    _blockedConnections = []
+    _freezed = gtk.FALSE
+    
+    def __init__(self, loadedImages, selectedImages, contextMenu):
+        self._locked = gtk.FALSE
+        self._contextMenu = contextMenu
+        self._selectedImages = selectedImages
+        widget = env.widgets["thumbnailList"]
+        self._thumbnailList = env.widgets["thumbnailList"]        
+        widget.connect("select_icon", self._widgetIconSelected)
+        widget.connect("unselect_icon", self._widgetIconUnselected)
+        widget.connect("button_press_event", self._button_pressed)
+        self.setModel(loadedImages)
+        
+    def setModel(self, loadedImages):
+        for c in self._modelConnections:
+            self._model.disconnect(c)
+        del self._modelConnections[:]
+        del self._blockedConnections[:]
+        self._model = loadedImages.model
+        self._initFromModel()
+        c = self._model.connect("row_inserted", self._initFromModel)
+        self._modelConnections.append(c)
+        c = self._model.connect("row_changed", self.on_row_changed)
+        self._modelConnections.append(c)
+        c = self._model.connect("row_deleted", self._initFromModel)
+        self._modelConnections.append(c)
+        c = self._model.connect("rows_reordered", self._initFromModel)
+        self._modelConnections.append(c)
+
+    def _initFromModel(self, *garbage):
+        self._thumbnailList.clear()
+        iter = self._model.get_iter_first()
+        for pos in range(self._model.iter_n_children(None)):
+            self._loadThumbnail(self._model, iter, pos)
+            iter = self._model.iter_next(iter)
+        self.selectionUpdated()
+        
+    def _blockModel(self):
+        for c in self._modelConnections:
+            self._model.handler_block(c)
+            self._blockedConnections.append(c)
+
+    def _unblockModel(self):
+        self._initFromModel()
+        for c in self._blockedConnections:
+            self._model.handler_unblock(c)
+        del self._blockedConnections[:]
+
+    def _loadThumbnail(self, model, iter, pos):
+        pixbuf = model.get_value(iter, Images.COLUMN_THUMBNAIL)
+        imageId = model.get_value(iter, Images.COLUMN_IMAGE_ID)
+        self._maxWidth = max(self._maxWidth, pixbuf.get_width())
+        self._thumbnailList.set_icon_width(self._maxWidth)
+        self._thumbnailList.insert_pixbuf(pos, pixbuf, "filnamn", str(imageId))
+
+    def freeze(self):
+        self._thumbnailList.freeze()
+        self._blockModel()
+
+    def thaw(self):
+        self._thumbnailList.thaw()
+        self._unblockModel()
+
+    def show(self):
+        self._locked = gtk.TRUE
+        self._thumbnailList.unselect_all()
+        self._locked = gtk.FALSE
+        env.widgets["thumbnailView"].show()
+        env.widgets["thumbnailList"].grab_focus()
+        self._unblockModel()
+        for image in self._model:
+            if image[Images.COLUMN_IMAGE_ID] in self._selectedImages:
+                env.widgets["thumbnailList"].moveto(image.path[0], 0.0)
+                break
+
+    def hide(self):
+        env.widgets["thumbnailView"].hide()
+        self._blockModel()
+        
+    def on_row_changed(self, model, path, iter):
+        self._locked = gtk.TRUE
+        self._thumbnailList.remove(path[0])
+        self._loadThumbnail(model, iter, path[0])
+        self._locked = gtk.FALSE
+        self.selectionUpdated()
+        
+    def _widgetIconSelected(self, widget, index, event):
+        if not self._locked:
+            self._locked = gtk.TRUE
+            iter = self._model.get_iter(index)
+            imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
+            self._selectedImages.add(imageId)
+            self._locked = gtk.FALSE
+
+    def _widgetIconUnselected(self, widget, index, event):
+        if not self._locked:
+            self._locked = gtk.TRUE        
+            iter = self._model.get_iter(index)
+            imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
+            try:
+                self._selectedImages.remove(imageId)
+            except(KeyError):
+                pass
+            self._locked = gtk.FALSE
+
+    def selectionUpdated(self):
+        if not self._locked:
+            self._locked = gtk.TRUE        
+            self._thumbnailList.unselect_all()
+            if len(self._model) > 0:
+                indices = xrange(sys.maxint)                
+                for image, index in zip(self._model, indices):
+                    if image[Images.COLUMN_IMAGE_ID] in self._selectedImages:
+                        self._thumbnailList.select_icon(index)
+            self._locked = gtk.FALSE            
+
+    def _button_pressed(self, widget, event):
+        if event.button == 3:
+            if self._contextMenu:
+                self._contextMenu.popup(None,None,None,event.button,event.time)
+            return gtk.FALSE
diff --git a/src/gnome/imagecontextmenu.py b/src/gnome/imagecontextmenu.py
deleted file mode 100644 (file)
index 0694771..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-import gtk
-import gobject
-import gtk
-
-from environment import env
-
-class ImageContextMenu(gtk.Menu):
-
-    def __init__(self, images, selectedImages):
-        gtk.Menu.__init__(self)
-
-        self._unregisterItem = gtk.MenuItem("Unregister")
-        self._unregisterItem.show()
-        self._unregisterItem.connect("activate", images.unregisterImages, None)
-        self.append(self._unregisterItem)
-
-        self._rotateLeftItem = gtk.MenuItem("Rotate left")
-        self._rotateLeftItem.show()
-        self._rotateLeftItem.connect("activate", images.rotate, 270)
-        self.append(self._rotateLeftItem)
-        
-        self._rotateRightItem = gtk.MenuItem("Rotate right")
-        self._rotateRightItem.show()
-        self._rotateRightItem.connect("activate", images.rotate, 90)
-        self.append(self._rotateRightItem)
-
-        self._tableViewGroup = None
-        self.tableViewSortItem = gtk.MenuItem("Sort by")
-        self._tableViewSortSubMenu = gtk.Menu()
-
-        sortAscendingItem = gtk.RadioMenuItem(None, "Ascending")
-        sortDescendingItem = gtk.RadioMenuItem(sortAscendingItem, "Descending")
-        sortAscendingItem.connect("activate", images.setSortOrder, gtk.SORT_ASCENDING)
-        sortDescendingItem.connect("activate", images.setSortOrder, gtk.SORT_DESCENDING)
-        sortAscendingItem.activate()
-        sortSeparator = gtk.SeparatorMenuItem()
-        sortAscendingItem.show()
-        sortDescendingItem.show()
-        sortSeparator.show()
-        self._tableViewSortSubMenu.append(sortAscendingItem)
-        self._tableViewSortSubMenu.append(sortDescendingItem)
-        self._tableViewSortSubMenu.append(sortSeparator)
-        self.tableViewSortItem.set_submenu(self._tableViewSortSubMenu)
-        self.tableViewSortItem.show()
-        self.append(self.tableViewSortItem)
-
-        
-        self.tableViewViewItem = gtk.MenuItem("View")
-        self._tableViewViewSubMenu = gtk.Menu()
-        self.tableViewViewItem.set_submenu(self._tableViewViewSubMenu)
-        self.tableViewViewItem.show()
-        self.append(self.tableViewViewItem)
-
-        self._images = images
-        self._selectedImages = selectedImages
-        self.updateContextMenu()
-
-        
-    def addTableViewColumn(self, name, modelColumn, widget):
-        # Populate the sort menu
-        sortItem = gtk.RadioMenuItem(self._tableViewGroup, name)
-        sortItem.connect("activate", self._images.sortByColumn, modelColumn)
-        if self._tableViewGroup == None:
-            self._tableViewGroup = sortItem
-        sortItem.show()
-        self._tableViewSortSubMenu.append(sortItem)
-        if name == env.defaultSortColumn:
-            sortItem.activate()
-        # Populate the view menu
-        viewItem = gtk.CheckMenuItem(name)
-        viewItem.connect("toggled", self._columnToggled, widget)
-        viewItem.show()
-        self._tableViewViewSubMenu.append(viewItem)
-        if name in env.defaultTableViewColumns:
-            viewItem.set_active(gtk.TRUE)
-        else:
-            viewItem.set_active(gtk.FALSE)
-        self._columnToggled(viewItem, widget)
-
-    def _columnToggled(self, checkMenuItem, widget):
-        widget.set_visible(checkMenuItem.get_active())
-        
-    def updateContextMenu(self):
-        if len(self._selectedImages) == 0:
-            self._unregisterItem.set_sensitive(gtk.FALSE)
-            self._rotateLeftItem.set_sensitive(gtk.FALSE)
-            self._rotateRightItem.set_sensitive(gtk.FALSE)
-        else:
-            self._unregisterItem.set_sensitive(gtk.TRUE)
-            self._rotateLeftItem.set_sensitive(gtk.TRUE)
-            self._rotateRightItem.set_sensitive(gtk.TRUE)
diff --git a/src/gnome/images.py b/src/gnome/images.py
deleted file mode 100644 (file)
index e77d820..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-import os
-import gtk
-import gobject
-import gc
-from sets import *
-from kofoto.imagecache import *
-from kofoto.shelf import *
-
-from environment import env
-from mysortedmodel import *
-
-class Images:
-    _unsortedModel = None
-    model = None
-    _title = ""
-    _imageCache = None
-    _sortColumn = None
-    
-    attributeNamesMap = {}
-
-    COLUMN_IMAGE_ID = 0
-    COLUMN_LOCATION = 1
-    COLUMN_THUMBNAIL = 2
-    COLUMN_VALID_LOCATION = 3
-    COLUMN_VALID_CHECKSUM = 4
-    COLUMN_ROW_EDITABLE   = 5
-    
-    _MANDATORY_COLUMNS_TYPE = [gobject.TYPE_INT,      # COLUMN_IMAGE_ID
-                               gobject.TYPE_STRING,   # COLUMN_LOCATION
-                               gtk.gdk.Pixbuf,        # COLUMN_THUMBNAIL
-                               gobject.TYPE_BOOLEAN,  # COLUMN_VALID_LOCATION
-                               gobject.TYPE_BOOLEAN,  # COLUMN_VALID_CHECKSUM
-                               gobject.TYPE_BOOLEAN]  # COLUMN_ROW_EDITABLE
-    
-
-    def __init__(self, selectedImages):
-        self._selectedImages = selectedImages
-        self._imageCache = ImageCache(env.imageCacheLocation)
-        self._loadModel()
-
-    def reloadModel(self):
-        self._loadModel()
-        env.controller.newImageModelLoaded()
-
-    def _loadModel(self):
-        columnsType = self._MANDATORY_COLUMNS_TYPE
-        allAttributeNames = Set(env.shelf.getAllAttributeNames())
-        allAttributeNames = allAttributeNames | Set(env.defaultTableViewColumns)
-        allAttributeNames = allAttributeNames | Set(env.defaultThumbnailViewColumns)
-        allAttributeNames = allAttributeNames | Set([env.defaultSortColumn])
-        for attributeName in allAttributeNames:
-            self.attributeNamesMap[attributeName] = len(columnsType)
-            columnsType.append(gobject.TYPE_STRING)
-        self._unsortedModel = gtk.ListStore(*columnsType)
-        self.model = MySortedModel(self._unsortedModel)
-
-    def loadImageList(self, imageList):
-        self._unsortedModel.clear()
-        gc.collect()
-        self._thumbnailSize = 0
-        for image in imageList:
-            iter = self._unsortedModel.append()
-            self._unsortedModel.set_value(iter, self.COLUMN_IMAGE_ID, image.getId()) 
-            self._unsortedModel.set_value(iter, self.COLUMN_LOCATION, image.getLocation())
-            self._unsortedModel.set_value(iter, self.COLUMN_ROW_EDITABLE, gtk.TRUE)
-            for attribute, value in image.getAttributeMap().items():
-                self._unsortedModel.set_value(iter, self.attributeNamesMap[attribute], value)
-                # TODO: update COLUMN_VALID_LOCATION
-
-    def loadThumbnails(self, wantedThumbnailSize):
-        iter = self._unsortedModel.get_iter_first()
-        while iter:
-            self._loadThumbnail(wantedThumbnailSize, iter)
-            iter = self._unsortedModel.iter_next(iter)
-        self._thumbnailSize = wantedThumbnailSize
-
-    def _loadThumbnail(self, wantedThumbnailSize, iter, reload=gtk.FALSE):
-        # Reload was used when thumbnail has been invalid and can not be reused.
-        # It is probably better to remove the "reload" parameter and instead
-        # remove thumbnails from the not-yet-existing-cache when needed.
-        try:
-            imageId = self._unsortedModel.get_value(iter, self.COLUMN_IMAGE_ID)
-            image = env.shelf.getImage(imageId)
-            thumbnailLocation = self._imageCache.get(image, wantedThumbnailSize)
-            pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnailLocation)
-            self._unsortedModel.set_value(iter, self.COLUMN_THUMBNAIL, pixbuf)
-        except IOError:
-            # TODO: Show some kind of error icon?
-            print "IOError"
-
-    def scalePixBuf(self, pixbuf, maxWidth, maxHeight):
-        scale = min(float(maxWidth) / pixbuf.get_width(), float(maxHeight) / pixbuf.get_height())
-        scale = min(1, scale)
-        if scale == 1:
-            return pixbuf
-        else:
-            return pixbuf.scale_simple(pixbuf.get_width() * scale,
-                                       pixbuf.get_height() * scale,
-                                       gtk.gdk.INTERP_BILINEAR) # gtk.gdk.INTERP_HYPER is slower but gives better quality.
-
-    
-    def unregisterImages(self, *foo):
-        # TODO Show dialog and ask for confirmation!
-        imageIdList = list(self._selectedImages)
-        self._selectedImages.clear()
-        for row in self._unsortedModel:
-            if row[self.COLUMN_IMAGE_ID] in imageIdList:
-                env.shelf.deleteImage(row[self.COLUMN_IMAGE_ID])
-                self._unsortedModel.remove(row.iter)
-
-    def rotate(self, button, angle):
-        # TODO: Make it possible for the user to configure if a rotation
-        # shall rotate the image or only update the orientation attribute?
-        for row in self._unsortedModel:
-            if row[self.COLUMN_IMAGE_ID] in self._selectedImages:
-                image = env.shelf.getImage(row[self.COLUMN_IMAGE_ID])
-                location = image.getLocation().encode(env.codeset)
-                # TODO: Read command from configuration file?
-                command = "jpegtran -rotate %s -perfect -copy all -outfile %s %s" % (angle, location, location)
-                result = os.system(command)
-                if result == 0:
-                    newHash = computeImageHash(location)
-                    image.setHash(newHash)
-                else:
-                    print "failed to execute:", command
-                self._loadThumbnail(100, row.iter, reload=gtk.TRUE)
-
-    def setSortOrder(self, widget, order):
-        self._sortOrder = order
-        if self._sortColumn:
-            self.sortByColumn(None, self._sortColumn)
-
-    def sortByColumn(self, menuItem, column):
-        self.model.set_sort_column_id(column, self._sortOrder)
-        self.model.set_sort_func(column, self._sort_func, column)
-        self._sortColumn = column
-
-    def _sort_func(self, model, iterA, iterB, column):
-        valueA = model.get_value(iterA, column)
-        valueB = model.get_value(iterB, column)
-        try:
-            result = cmp(float(valueA), float(valueB))
-        except (ValueError, TypeError):
-            result = cmp(valueA, valueB)
-        if result == 0:
-            result = cmp(model.get_value(iterA, self.COLUMN_IMAGE_ID),
-                         model.get_value(iterB, self.COLUMN_IMAGE_ID))
-        return result
diff --git a/src/gnome/imageselection.py b/src/gnome/imageselection.py
deleted file mode 100644 (file)
index 7718624..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-from environment import env
-from kofoto.sets import *
-
-class ImageSelection:
-    def __init__(self, changedCallback):
-        self._set = Set()
-        self._changedCallback = changedCallback
-
-    def clear(self):
-        self._set.clear()
-        self._changedCallback()
-
-    def set(self, imageIdList):
-        self._set.clear()
-        for imageId in imageIdList:
-            self._set.add(imageId)
-        self._changedCallback()
-
-    def add(self, imageId):
-        self._set.add(imageId)
-        self._changedCallback()
-
-    def remove(self, imageId):
-        self._set.remove(imageId)
-        self._changedCallback()
-
-    def __contains__(self, imageId):
-        return imageId in self._set
-
-    def __len__(self):
-        return len(self._set)
-
-    def __iter__(self):
-        return self._set.__iter__()
-        
diff --git a/src/gnome/imageview.py b/src/gnome/imageview.py
deleted file mode 100644 (file)
index 5b408e1..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-import gtk
-import gtk.gdk
-import math
-import gobject
-import gc
-from gtk import TRUE, FALSE
-from environment import env
-
-class ImageView(gtk.ScrolledWindow):
-    # TODO: Read from configuration file?
-    _INTERPOLATION_TYPE = gtk.gdk.INTERP_BILINEAR
-    # gtk.gdk.INTERP_HYPER is slower but gives better quality.
-    _MAX_IMAGE_SIZE = 2000
-    _MIN_IMAGE_SIZE = 1
-    _MIN_ZOOM = -100
-    _MAX_ZOOM = 1
-    _ZOOMFACTOR = 1.2
-    
-    _pixBuf = None
-    _currentZoom = None
-    _wantedZoom = None
-    _image = gtk.Image()            
-    _fitToWindowMode = TRUE
-    _previousWidgetWidth = 0
-    _previousWidgetHeight = 0
-    
-    def __init__(self):
-        gtk.ScrolledWindow.__init__(self)
-        eventBox = gtk.EventBox()
-        eventBox.add(self._image)
-        self.add_with_viewport(eventBox)
-        self.add_events(gtk.gdk.ALL_EVENTS_MASK)
-        self.connect_after("size_allocate", self.resizeEventHandler)
-        self.connect("scroll_event", self.scrollEventHandler)
-
-    def loadFile(self, filename):
-        # TODO: Loading file should be asyncronous to avoid freezing the gtk-main loop
-        try:
-            gc.collect()
-            self._pixBuf = gtk.gdk.pixbuf_new_from_file(filename.encode(env.codeset))
-            self._image.show()
-            self._newImageLoaded = TRUE
-            self.fitToWindow()
-        except gobject.GError:
-            print "GError while loading ", filename
-            self._pixBuf = None
-            self._image.hide()
-
-    def clear(self):
-        self._image.hide()
-        self._image.set_from_file(None)
-        self._pixBuf = None
-        gc.collect()
-            
-    def renderImage(self):
-        # TODO: Scaling should be asyncronous to avoid freezing the gtk-main loop
-        if self._pixBuf == None:
-            # No image loaded
-            self._image.hide()
-            return
-        if self._currentZoom == self._wantedZoom and self._newImageLoaded == FALSE:
-            return
-        if self._wantedZoom == 0:
-            pixBufResized = self._pixBuf
-        else:
-            zoomMultiplicator = pow(self._ZOOMFACTOR, self._wantedZoom)
-            wantedWidth = int(self._pixBuf.get_width() * zoomMultiplicator)
-            wantedHeight = int(self._pixBuf.get_height() * zoomMultiplicator)
-            if min(wantedWidth, wantedHeight) < self._MIN_IMAGE_SIZE:
-                # Too small image size
-                return
-            if max(wantedWidth, wantedHeight) > self._MAX_IMAGE_SIZE:
-                # Too large image size
-                return
-            pixBufResized = self._pixBuf.scale_simple(wantedWidth,
-                                                      wantedHeight,
-                                                      self._INTERPOLATION_TYPE)
-        pixMap, mask = pixBufResized.render_pixmap_and_mask()
-        self._image.set_from_pixmap(pixMap, mask)
-        self._newImageLoaded = FALSE
-        self._currentZoom = self._wantedZoom
-        gc.collect()
-
-    def resizeEventHandler(self, widget, gdkEvent):
-        if self._fitToWindowMode == TRUE:
-            x, y, width, height = self.get_allocation()
-            if height != self._previousWidgetHeight or width != self._previousWidgetWidth:
-                self.fitToWindow()
-        return FALSE
-        
-    def fitToWindow(self, *foo):
-        self._fitToWindowMode = TRUE
-        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
-        y, x, widgetWidth, widgetHeight = self.get_allocation()
-        if self._pixBuf != None:
-            self._previousWidgetWidth = widgetWidth
-            self._previousWidgetHeight = widgetHeight
-            a = min(float(widgetWidth) / self._pixBuf.get_width(),
-                    float(widgetHeight) / self._pixBuf.get_height())
-            self._wantedZoom = self._log(self._ZOOMFACTOR, a)
-            self._wantedZoom = min(self._wantedZoom, 0)
-            self._wantedZoom = max(self._wantedZoom, self._MIN_ZOOM)
-            self.renderImage()
-        
-    def _log(self, base, value):
-        return math.log(value) / math.log(base)
-
-    def zoomIn(self, *foo):
-        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self._fitToWindowMode = FALSE
-        if self._wantedZoom <= self._MAX_ZOOM:
-            self._wantedZoom = math.floor(self._wantedZoom + 1)
-            self.renderImage()
-                
-    def zoomOut(self, *foo):
-        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self._fitToWindowMode = FALSE
-        if self._wantedZoom >= self._MIN_ZOOM:
-            self._wantedZoom = math.ceil(self._wantedZoom - 1)
-            self.renderImage()
-
-    def zoom100(self, *foo):
-        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self._fitToWindowMode = FALSE
-        self._wantedZoom = 0
-        self.renderImage()
-                    
-    def scrollEventHandler(self, widget, gdkEvent):
-        if gdkEvent.type == gtk.gdk.SCROLL:
-            if gdkEvent.direction == gtk.gdk.SCROLL_UP:
-                self.zoomOut()
-            elif gdkEvent.direction == gtk.gdk.SCROLL_DOWN:
-                self.zoomIn()
-            return TRUE
-        else:
-            return FALSE
diff --git a/src/gnome/mainwindow.py b/src/gnome/mainwindow.py
deleted file mode 100644 (file)
index 6ea7acc..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-import gtk
-import gtk.gdk
-
-from environment import env
-
-class MainWindow(gtk.Window):
-    def __init__(self):
-        self._toggleLock = gtk.FALSE
-        
-        env.widgets["expandViewToggleButton"].connect("toggled", self._toggleExpandView)
-        env.widgets["expandViewToggleButton"].get_child().add(self.getIconImage("fullscreen-24.png"))
-        env.widgets["attributeToggleButton"].connect("toggled", self._toggleAttributesView)
-        env.widgets["thumbnailsViewToggleButton"].connect("clicked", self._toggleThumbnailsView)
-        env.widgets["imageViewToggleButton"].connect("clicked", self._toggleImageView)
-        env.widgets["tableViewToggleButton"].connect("clicked", self._toggleTableView)
-        env.widgets["save"].connect("activate", env.controller.save)
-        env.widgets["quit"].connect("activate", env.controller.quit)
-
-        # Gray out not yet implemented stuff...
-        env.widgets["revert"].set_sensitive(gtk.FALSE)
-        env.widgets["copy"].set_sensitive(gtk.FALSE)
-        env.widgets["paste"].set_sensitive(gtk.FALSE)
-        env.widgets["preferences"].set_sensitive(gtk.FALSE)
-        
-    def getIconImage(self, name):
-        pixbuf = gtk.gdk.pixbuf_new_from_file(env.iconDir + name)
-        image = gtk.Image()
-        image.set_from_pixbuf(pixbuf)
-        image.show()
-        return image
-
-    def _toggleExpandView(self, button):
-        if button.get_active():
-            env.widgets["sourceNotebook"].hide()
-        else:
-            env.widgets["sourceNotebook"].show()
-
-    def _toggleAttributesView(self, button):
-        if button.get_active():
-            env.widgets["attributeView"].show()
-        else:
-            env.widgets["attributeView"].hide()
-
-    def _toggleThumbnailsView(self, button):
-        if not self._toggleLock:
-            self._toggleLock = gtk.TRUE
-            button.set_active(gtk.TRUE)
-            env.widgets["imageViewToggleButton"].set_active(gtk.FALSE)
-            env.widgets["tableViewToggleButton"].set_active(gtk.FALSE)
-            env.controller.showThumbnailView()
-            self._toggleLock = gtk.FALSE
-
-    def _toggleImageView(self, button):
-        if not self._toggleLock:
-            self._toggleLock = gtk.TRUE
-            button.set_active(gtk.TRUE)
-            env.widgets["tableViewToggleButton"].set_active(gtk.FALSE)
-            env.widgets["thumbnailsViewToggleButton"].set_active(gtk.FALSE)
-            env.controller.showSingleImageView()
-            self._toggleLock = gtk.FALSE
-            
-    def _toggleTableView(self, button):
-        if not self._toggleLock:
-            self._toggleLock = gtk.TRUE
-            button.set_active(gtk.TRUE)
-            env.widgets["thumbnailsViewToggleButton"].set_active(gtk.FALSE)
-            env.widgets["imageViewToggleButton"].set_active(gtk.FALSE)
-            env.controller.showTableView()
-            self._toggleLock = gtk.FALSE
diff --git a/src/gnome/mysortedmodel.py b/src/gnome/mysortedmodel.py
deleted file mode 100644 (file)
index a7c7776..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-import gtk
-
-class MySortedModel(gtk.TreeModelSort):
-
-    def __init__(self, model):
-        gtk.TreeModelSort.__init__(self, model)
-        self._model = model
-    
-    def __getitem__(self, path):
-        child_path = self.convert_path_to_child_path(path)
-        if child_path:
-            return self._model[child_path]
-        else:
-            raise IndexError
-
-    def set_value(self, iter, column, value):
-        childIter = self._model.get_iter_first()
-        self.convert_iter_to_child_iter(childIter, iter)
-        self._model.set_value(childIter, column, value)
-
-    # Workaround until http://bugzilla.gnome.org/show_bug.cgi?id=121633 is solved.
-    def get_iter_first(self):
-        if len(self) > 0:
-            return gtk.TreeModelSort.get_iter_first(self)
-        else:
-            return None
-
-    # Workaround until http://bugzilla.gnome.org/show_bug.cgi?id=121633 is solved.       
-    def __iter__(self):
-        if len(self._model) > 0:
-            return gtk.TreeModelSort.__iter__(self)
-        else:
-            return self._model.__iter__()
diff --git a/src/gnome/singleimageview.py b/src/gnome/singleimageview.py
deleted file mode 100644 (file)
index 1a76d34..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-
-import gtk
-import sys
-
-from environment import env
-from images import *
-from imageview import *
-
-# TODO: Prenumerera på model changes för att veta när tex en bild roterats
-
-class SingleImageView(ImageView):
-    _modelConnections = []
-    
-    def __init__(self, loadedImages, selectedImages, contextMenu):
-        ImageView.__init__(self)
-        self._locked = gtk.FALSE
-        self._freezed = gtk.FALSE
-        env.widgets["imageView"].add(self)
-        self.show_all()
-        self._selectedImages = selectedImages
-        self._contextMenu = contextMenu
-        env.widgets["nextButton"].connect("clicked", self._goto, 1)
-        env.widgets["previousButton"].connect("clicked", self._goto, -1)
-        env.widgets["zoomToFit"].connect("clicked", self.fitToWindow)
-        env.widgets["zoom100"].connect("clicked", self.zoom100)
-        env.widgets["zoomIn"].connect("clicked", self.zoomIn)
-        env.widgets["zoomOut"].connect("clicked", self.zoomOut)
-        self.connect("button_press_event", self._button_pressed)
-        self.setModel(loadedImages)
-    
-    def setModel(self, loadedImages):
-        for c in self._modelConnections:
-            self._model.disconnect(c)
-        del self._modelConnections[:]
-        self._model = loadedImages.model
-        c = self._model.connect("row_inserted", self._modelRowsChanged)
-        self._modelConnections.append(c)
-        c = self._model.connect("row_changed", self.selectionUpdated)
-        self._modelConnections.append(c)
-        c = self._model.connect("row_deleted", self._modelRowsChanged)
-        self._modelConnections.append(c)
-        self._modelRowsChanged()
-
-    def _modelRowsChanged(self, *foo):
-        if len(self._selectedImages) != 0:
-            self._selectedImages.clear()
-        self.selectionUpdated()
-        
-    def selectionUpdated(self, *foo):
-        if not self._freezed and not self._locked:
-            self._locked = gtk.TRUE
-            try:
-                if len(self._selectedImages) == 0:
-                    imageId = self._model[0][Images.COLUMN_IMAGE_ID]
-                    self._selectedImages.set([imageId])
-                else:
-                    imageId = list(self._selectedImages)[0]
-                    self._selectedImages.set([imageId])
-                self._imageId = imageId
-                self._updateButtons(self._getRow())
-                self._loadImage()
-            except(IndexError):
-                self.clear()
-                env.widgets["previousButton"].set_sensitive(gtk.FALSE)
-                env.widgets["nextButton"].set_sensitive(gtk.FALSE)
-            self._locked = gtk.FALSE
-
-    def _goto(self, button, direction):
-        if not self._locked:
-            self._locked = gtk.TRUE        
-            row = self._getRow() + direction
-            self._updateButtons(row)
-            self._imageId = self._model[row][Images.COLUMN_IMAGE_ID]
-            self._loadImage()
-            self._selectedImages.set([self._imageId])
-            self._locked = gtk.FALSE
-
-    def _getRow(self):
-        for image in self._model:
-            if image[Images.COLUMN_IMAGE_ID] == self._imageId:
-                return image.path[0]
-        raise IndexError
-
-    def _updateButtons(self, row):
-        if row <= 0:
-            env.widgets["previousButton"].set_sensitive(gtk.FALSE)
-        else: 
-            env.widgets["previousButton"].set_sensitive(gtk.TRUE)
-        if row >= len(self._model) - 1:
-            env.widgets["nextButton"].set_sensitive(gtk.FALSE)
-        else:
-            env.widgets["nextButton"].set_sensitive(gtk.TRUE)
-            
-    def _loadImage(self):
-        image = env.shelf.getImage(self._imageId)
-        self.loadFile(image.getLocation())
-        
-    def freeze(self):
-        self._freezed = gtk.TRUE
-
-    def thaw(self):
-        self._freezed = gtk.FALSE
-        self.selectionUpdated()
-
-    def show(self):
-        self.thaw()
-        env.widgets["imageView"].show()
-        env.widgets["imageView"].grab_focus()
-        env.widgets["zoom100"].set_sensitive(gtk.TRUE)
-        env.widgets["zoomToFit"].set_sensitive(gtk.TRUE)
-        env.widgets["zoomIn"].set_sensitive(gtk.TRUE)
-        env.widgets["zoomOut"].set_sensitive(gtk.TRUE)
-        self.thaw()
-
-    def hide(self):
-        self.freeze()
-        self.clear()
-        env.widgets["imageView"].hide()
-        env.widgets["previousButton"].set_sensitive(gtk.FALSE)
-        env.widgets["nextButton"].set_sensitive(gtk.FALSE)
-        env.widgets["zoom100"].set_sensitive(gtk.FALSE)
-        env.widgets["zoomToFit"].set_sensitive(gtk.FALSE)
-        env.widgets["zoomIn"].set_sensitive(gtk.FALSE)
-        env.widgets["zoomOut"].set_sensitive(gtk.FALSE)
-        self.freeze()
-
-    def _button_pressed(self, widget, event):
-        if event.button == 3:
-            if self._contextMenu:
-                self._contextMenu.popup(None,None,None,event.button,event.time)
-            return gtk.TRUE
diff --git a/src/gnome/source.py b/src/gnome/source.py
deleted file mode 100644 (file)
index e62c0bd..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-import string
-import gtk
-import gtk.gdk
-
-from environment import env
-from kofoto.search import *
-from kofoto.shelf import *
-
-class Source:
-    def __init__(self):
-        self._hasChanged = gtk.FALSE
-        # Using "focus-out-event" as a workaround, since there is no
-        # "editing_done" signal
-        env.widgets["sourceEntry"].connect("focus-out-event", self._updated)
-        env.widgets["sourceEntry"].connect("changed", self._changed)
-        
-    def _changed(self, *foo):
-        self._hasChanged = gtk.TRUE
-        
-    def _updated(self, widget=None, *foo):
-        if not self._hasChanged:
-            return
-        if widget==None:
-            widget = env.widgets["sourceEntry"]
-        source = widget.get_text()
-        l = string.split(source, u"://", 1)
-        imageList = []
-        if l[0] == u"query":
-            parser = Parser(env.shelf)
-            try:
-                for child in env.shelf.search(parser.parse(l[1])):
-                    if not child.isAlbum():
-                        imageList.append(child)
-            except(CategoryDoesNotExistError):
-                print "Unkown categories: ", l[1]
-                imageList = []
-            except(ParseError):
-                print "Invalid query: ", l[1]
-                imageList = []
-        elif l[0] == u"album":
-            try:
-                album = env.shelf.getAlbum(l[1])
-                for child in album.getChildren():
-                    if not child.isAlbum():
-                        imageList.append(child)
-            except(AlbumDoesNotExistError):
-                print "Unknown album: ", l[1]
-        else:
-            print "Unknown protocoll"
-        env.controller.loadImages(imageList)
-        self._hasChanged = gtk.FALSE
-                
-    def set(self, source):
-        env.widgets["sourceEntry"].set_text(source)        
-        self._updated()
diff --git a/src/gnome/tableview.py b/src/gnome/tableview.py
deleted file mode 100644 (file)
index 88db47f..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-import gtk
-
-from environment import env
-from images import *
-from kofoto.sets import *
-
-class TableView:
-
-    def __init__(self, loadedImages, selectedImages, contextMenu):
-        self._locked = gtk.FALSE
-        self._tableView = env.widgets["tableView"]
-        selection = self._tableView.get_selection()
-        selection.set_mode(gtk.SELECTION_MULTIPLE)
-        self._contextMenu = contextMenu
-        self._selectedImages = selectedImages
-        self._tableView.connect("button_press_event", self._button_pressed)
-        selection.connect('changed', self._widgetSelectionUpdated)
-        self.setModel(loadedImages)
-
-    def setModel(self, loadedImages):
-        self._model = loadedImages.model
-        self._tableView.set_model(self._model)
-        for column in self._tableView.get_columns():
-            self._tableView.remove_column(column)
-        self._createThumbnailColumn()
-        self._createIdColumn()
-        self._createLocationColumn()
-        self._createAttributeColumns(loadedImages.attributeNamesMap)
-
-    def _createThumbnailColumn(self):
-        renderer = gtk.CellRendererPixbuf()
-        column = gtk.TreeViewColumn("Thumbnail", renderer, pixbuf=Images.COLUMN_THUMBNAIL)
-        column.set_reorderable(gtk.TRUE)
-        self._tableView.append_column(column)
-
-    def _createIdColumn(self):
-        renderer = gtk.CellRendererText()
-        columnName = u"ImageId"
-        column = gtk.TreeViewColumn(columnName, renderer, text=Images.COLUMN_IMAGE_ID)
-        column.set_resizable(gtk.TRUE)
-        column.set_reorderable(gtk.TRUE)
-        self._contextMenu.addTableViewColumn(columnName, Images.COLUMN_IMAGE_ID, column)
-        self._tableView.append_column(column)
-
-    def _createLocationColumn(self):
-        renderer = gtk.CellRendererText()
-        columnName = u"Location"
-        column = gtk.TreeViewColumn(columnName, renderer,
-                                    text=Images.COLUMN_LOCATION)
-        column.set_resizable(gtk.TRUE)
-        column.set_reorderable(gtk.TRUE)
-        self._contextMenu.addTableViewColumn(columnName, Images.COLUMN_LOCATION, column)
-        self._tableView.append_column(column)        
-
-    def _createAttributeColumns(self, attributeNamesMap):
-        allAttributeNames = attributeNamesMap.keys()
-        allAttributeNames.sort()
-        for attributeName in allAttributeNames:
-            columnNumber = attributeNamesMap[attributeName]
-            renderer = gtk.CellRendererText()
-            column = gtk.TreeViewColumn(attributeName,
-                                        renderer,
-                                        text=columnNumber,
-                                        editable=Images.COLUMN_ROW_EDITABLE)
-            column.set_resizable(gtk.TRUE)
-            column.set_reorderable(gtk.TRUE)
-            renderer.connect("edited", self._attribute_editing_done,
-                             columnNumber,
-                             attributeName)
-            self._contextMenu.addTableViewColumn(attributeName, columnNumber, column)
-            self._tableView.append_column(column)
-
-    def _attribute_editing_done(self, renderer, path, value, column, attributeName):
-        iter = self._model.get_iter(path)
-        oldValue = self._model.get_value(iter, column)
-        if not oldValue:
-            oldValue = u""
-        value = unicode(value, "utf-8")
-        if oldValue != value:
-            # TODO Show dialog and ask for confirmation?
-            imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
-            image = env.shelf.getImage(imageId)
-            image.setAttribute(attributeName, value)
-            self._model.set_value(iter, column, value)
-            
-    def freeze(self):
-        pass
-
-    def thaw(self):
-        pass
-
-    def show(self):
-        env.widgets["tableViewScroll"].show()
-        self._contextMenu.tableViewViewItem.show()
-        self._tableView.grab_focus()
-        for image in self._model:
-            if image[Images.COLUMN_IMAGE_ID] in self._selectedImages:
-                self._tableView.scroll_to_cell(image.path, None, gtk.TRUE, 0, 0)
-                break
-
-    def hide(self):
-        env.widgets["tableViewScroll"].hide()
-        self._contextMenu.tableViewViewItem.hide()
-        
-    def selectionUpdated(self):
-        if not self._locked:
-            self._locked = gtk.TRUE
-            selection = self._tableView.get_selection()
-            selection.unselect_all()
-            iter = self._model.get_iter_first()
-            index = 0
-            while iter:
-                imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
-                if imageId in self._selectedImages:
-                    selection.select_path(index)
-                index = index + 1
-                iter =  self._model.iter_next(iter)
-            self._locked = gtk.FALSE
-
-    def _widgetSelectionUpdated(self, selection):
-        if not self._locked:
-            self._locked = gtk.TRUE
-            iter = self._model.get_iter_first()
-            imageIdList = []
-            while iter:
-                if selection.iter_is_selected(iter):
-                    imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
-                    imageIdList.append(imageId)
-                iter = self._model.iter_next(iter)
-            self._selectedImages.set(imageIdList)
-            self._locked = gtk.FALSE
-        
-    def _button_pressed(self, widget, event):
-        if event.button == 3:
-            self._contextMenu.popup(None,None,None,event.button,event.time)
-            return gtk.TRUE
diff --git a/src/gnome/thumbnailview.py b/src/gnome/thumbnailview.py
deleted file mode 100644 (file)
index b3b501b..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-import gtk
-import sys
-
-from environment import env
-from images import *
-
-class ThumbnailView:
-    _maxWidth = 0
-    _modelConnections = []
-    _blockedConnections = []
-    _freezed = gtk.FALSE
-    
-    def __init__(self, loadedImages, selectedImages, contextMenu):
-        self._locked = gtk.FALSE
-        self._contextMenu = contextMenu
-        self._selectedImages = selectedImages
-        widget = env.widgets["thumbnailList"]
-        self._thumbnailList = env.widgets["thumbnailList"]        
-        widget.connect("select_icon", self._widgetIconSelected)
-        widget.connect("unselect_icon", self._widgetIconUnselected)
-        widget.connect("button_press_event", self._button_pressed)
-        self.setModel(loadedImages)
-        
-    def setModel(self, loadedImages):
-        for c in self._modelConnections:
-            self._model.disconnect(c)
-        del self._modelConnections[:]
-        del self._blockedConnections[:]
-        self._model = loadedImages.model
-        self._initFromModel()
-        c = self._model.connect("row_inserted", self._initFromModel)
-        self._modelConnections.append(c)
-        c = self._model.connect("row_changed", self.on_row_changed)
-        self._modelConnections.append(c)
-        c = self._model.connect("row_deleted", self._initFromModel)
-        self._modelConnections.append(c)
-        c = self._model.connect("rows_reordered", self._initFromModel)
-        self._modelConnections.append(c)
-
-    def _initFromModel(self, *garbage):
-        self._thumbnailList.clear()
-        iter = self._model.get_iter_first()
-        for pos in range(self._model.iter_n_children(None)):
-            self._loadThumbnail(self._model, iter, pos)
-            iter = self._model.iter_next(iter)
-        self.selectionUpdated()
-        
-    def _blockModel(self):
-        for c in self._modelConnections:
-            self._model.handler_block(c)
-            self._blockedConnections.append(c)
-
-    def _unblockModel(self):
-        self._initFromModel()
-        for c in self._blockedConnections:
-            self._model.handler_unblock(c)
-        del self._blockedConnections[:]
-
-    def _loadThumbnail(self, model, iter, pos):
-        pixbuf = model.get_value(iter, Images.COLUMN_THUMBNAIL)
-        imageId = model.get_value(iter, Images.COLUMN_IMAGE_ID)
-        self._maxWidth = max(self._maxWidth, pixbuf.get_width())
-        self._thumbnailList.set_icon_width(self._maxWidth)
-        self._thumbnailList.insert_pixbuf(pos, pixbuf, "filnamn", str(imageId))
-
-    def freeze(self):
-        self._thumbnailList.freeze()
-        self._blockModel()
-
-    def thaw(self):
-        self._thumbnailList.thaw()
-        self._unblockModel()
-
-    def show(self):
-        self._locked = gtk.TRUE
-        self._thumbnailList.unselect_all()
-        self._locked = gtk.FALSE
-        env.widgets["thumbnailView"].show()
-        env.widgets["thumbnailList"].grab_focus()
-        self._unblockModel()
-        for image in self._model:
-            if image[Images.COLUMN_IMAGE_ID] in self._selectedImages:
-                env.widgets["thumbnailList"].moveto(image.path[0], 0.0)
-                break
-
-    def hide(self):
-        env.widgets["thumbnailView"].hide()
-        self._blockModel()
-        
-    def on_row_changed(self, model, path, iter):
-        self._locked = gtk.TRUE
-        self._thumbnailList.remove(path[0])
-        self._loadThumbnail(model, iter, path[0])
-        self._locked = gtk.FALSE
-        self.selectionUpdated()
-        
-    def _widgetIconSelected(self, widget, index, event):
-        if not self._locked:
-            self._locked = gtk.TRUE
-            iter = self._model.get_iter(index)
-            imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
-            self._selectedImages.add(imageId)
-            self._locked = gtk.FALSE
-
-    def _widgetIconUnselected(self, widget, index, event):
-        if not self._locked:
-            self._locked = gtk.TRUE        
-            iter = self._model.get_iter(index)
-            imageId = self._model.get_value(iter, Images.COLUMN_IMAGE_ID)
-            try:
-                self._selectedImages.remove(imageId)
-            except(KeyError):
-                pass
-            self._locked = gtk.FALSE
-
-    def selectionUpdated(self):
-        if not self._locked:
-            self._locked = gtk.TRUE        
-            self._thumbnailList.unselect_all()
-            if len(self._model) > 0:
-                indices = xrange(sys.maxint)                
-                for image, index in zip(self._model, indices):
-                    if image[Images.COLUMN_IMAGE_ID] in self._selectedImages:
-                        self._thumbnailList.select_icon(index)
-            self._locked = gtk.FALSE            
-
-    def _button_pressed(self, widget, event):
-        if event.button == 3:
-            if self._contextMenu:
-                self._contextMenu.popup(None,None,None,event.button,event.time)
-            return gtk.FALSE