Exemple #1
0
class UpdateDialog(KDialog):
    def __init__(self, mainWindow, renderThread, pluginConfig=None):
        KDialog.__init__(self, mainWindow)
        self.renderThread = renderThread

        self.databaseUrl = None
        if pluginConfig:
            self.databaseUrl = util.readConfigString(self.pluginConfig,
                "Update database url", None)

        if not self.databaseUrl:
            self.databaseUrl = unicode('sqlite:///'
                + util.getLocalData('dictionaries.db'))

        self.renderThread.setObject(DictionaryInfo,
            databaseUrl=self.databaseUrl)

        self.setCaption(i18n("Install/Update Dictionaries"))
        self.setButtons(KDialog.ButtonCode(KDialog.Close))
        self.enableButton(KDialog.Cancel, False)

        # TODO can we defer the creation of the update widget until the dialog is shown?
        self.updateWidget = UpdateWidget(mainWindow, renderThread, pluginConfig)
        self.connect(self.updateWidget, SIGNAL("working(bool)"),
            self.slotUpdateWorking)
        self.setMainWidget(self.updateWidget)

        self.connect(self, SIGNAL("finished()"), self.slotFinish)

        self.initialised = False

        self.connect(self.renderThread, SIGNAL("jobFinished"),
            self.contentRendered)
        self.connect(self.renderThread, SIGNAL("jobErrorneous"),
            self.renderingFailed)

        self.actionCollection = KActionCollection(self)
        self.setupActions()

    def showEvent(self, event):
        if not self.initialised:
            self.initialised = True
            self.loadDatabaseBuilder()
            self.updateWidget.setup()

    def setupActions(self):
        # update dictionaries
        self._updateAction = KAction(KIcon('system-software-update'),
            i18n("&Update..."), self)
        self.actionCollection.addAction("updatedictionaries",
            self._updateAction)
        self._updateAction.setWhatsThis(
            i18n("Download and update dictionaries."))
        self.connect(self._updateAction, SIGNAL("triggered(bool)"),
            self.exec_)
        # optimise database
        self._optimiseAction = KAction(KIcon('system-run'),
            i18n("&Optimise database"), self)
        self.actionCollection.addAction("optimisedatabase",
            self._optimiseAction)
        self._optimiseAction.setWhatsThis(
            i18n("Rearranges and optimises the database."))
        self._optimiseAction.setEnabled(True) # TODO
        self.connect(self._optimiseAction, SIGNAL("triggered(bool)"),
            self.slotOptimiseDatabase)

    def updateAction(self, actionCollection):
        actionCollection.addAction(self._updateAction.objectName(),
            self._updateAction)
        return self._updateAction

    def optimiseAction(self, actionCollection):
        actionCollection.addAction(self._optimiseAction.objectName(),
            self._optimiseAction)
        return self._optimiseAction

    def slotUpdateWorking(self, working):
        if working:
            self.setButtons(KDialog.ButtonCode(KDialog.Cancel))
        else:
            self.setButtons(KDialog.ButtonCode(KDialog.Close))

    def slotFinish(self):
        if self.updateWidget.isWorking():
            self.updateWidget.cancel()

    def slotOptimiseDatabase(self):
        self.loadDatabaseBuilder()
        dbBuild = self.renderThread.getObjectInstance(build.DatabaseBuilder)
        if dbBuild.isOptimizable():
            if KMessageBox.warningContinueCancel(self,
                i18n("This operation might take some time."),
                i18n("Optimise Database"), KStandardGuiItem.cont(),
                KStandardGuiItem.cancel(), 'database_optimise') \
                    == KMessageBox.Continue:
                QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
                self.currentJob = self.renderThread.enqueue(
                    build.DatabaseBuilder, 'optimize')

    def loadDatabaseBuilder(self):
        if not self.renderThread.hasObject(build.DatabaseBuilder):
            options = EclectusCommandLineBuilder.getDefaultOptions()

            db = getDBConnector(getDatabaseConfiguration(self.databaseUrl))

            self.renderThread.setObject(build.DatabaseBuilder, dbConnectInst=db,
                **options)

    def contentRendered(self, id, classObject, method, args, param, content):
        if classObject == build.DatabaseBuilder and method == 'optimize':
            QApplication.restoreOverrideCursor()

    def renderingFailed(self, id, classObject, method, args, param, e,
            stacktrace):
        if classObject == build.DatabaseBuilder and method == 'optimize':
            print >>sys.stderr, stacktrace
            QApplication.restoreOverrideCursor()
class MainWindow(KXmlGuiWindow):

    "Class which displays the main Danbooru Client window."

    def __init__(self, *args):

        "Initialize a new main window."

        super(MainWindow, self).__init__(*args)
        self.cache = KPixmapCache("danbooru")
        self.preferences = preferences.Preferences()
        self.api = None
        self.__ratings = None
        self.__step = 0

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no

        self.statusbar = self.statusBar()
        self.progress = QProgressBar()
        self.thumbnailarea = None
        self.tag_dock = None
        self.pool_dock = None
        self.first_fetch_widget = None

        self.progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # FIXME: Hackish, but how to make it small otherwise?
        self.progress.setMinimumSize(100, 1)
        self.statusbar.addPermanentWidget(self.progress)
        self.progress.hide()

        self.setup_welcome_widget()
        self.setup_actions()

    def reload_config(self):
        """Reload configuration after a change"""

        urls = self.preferences.boards_list

        if self.first_fetch_widget is not None:
            self.first_fetch_widget.setup_urls(urls)

        if self.thumbnailarea is not None:

            max_thumbnail = self.preferences.thumbnail_no
            max_rating = self.preferences.max_allowed_rating

            self.thumbnailarea.fetchwidget.limit = max_thumbnail
            self.thumbnailarea.fetchwidget.rating = max_rating
            self.thumbnailarea.fetchwidget.update_values()

            self.thumbnailarea.connectwidget.setup_urls(urls)

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no

    def setup_welcome_widget(self):
        """Load the welcome widget at startup."""

        widget = QWidget()
        layout = QVBoxLayout()

        welcome = QLabel(parent=self)
        pix = QPixmap(KStandardDirs.locate("appdata", "logo.png"))

        welcome.setPixmap(pix)
        welcome.setAlignment(Qt.AlignCenter)

        self.first_fetch_widget = connectwidget.ConnectWidget(
            self.preferences.boards_list, self)

        self.statusbar.addPermanentWidget(self.first_fetch_widget, 300)

        self.first_fetch_widget.connectionEstablished.connect(
            self.handle_connection)
        self.first_fetch_widget.rejected.connect(self.first_fetch_widget.hide)

        self.first_fetch_widget.hide()

        self.first_fetch_widget.setSizePolicy(QSizePolicy.Preferred,
                                              QSizePolicy.Fixed)

        layout.addWidget(self.first_fetch_widget)
        layout.addWidget(welcome)
        widget.setLayout(layout)

        self.setCentralWidget(widget)

    def setup_tooltips(self):
        """Set tooltips for the actions."""

        self.connect_action.setToolTip(i18n("Connect to a Danbooru board"))
        self.fetch_action.setToolTip(
            i18n("Fetch thumbnails from a Danbooru board"))
        self.batch_download_action.setToolTip(i18n("Batch download images"))

    def create_actions(self):
        """Create actions for the main window."""

        self.connect_action = KAction(KIcon("document-open-remote"),
                                      i18n("Connect"), self)
        self.fetch_action = KAction(KIcon("download"), i18n("Download"), self)
        self.clean_action = KAction(KIcon("trash-empty"),
                                    i18n("Clear thumbnail cache"), self)
        self.batch_download_action = KAction(KIcon("download"),
                                             i18n("Batch download"), self)
        self.pool_toggle_action = KToggleAction(KIcon("image-x-generic"),
                                                i18n("Pools"), self)
        self.tag_display_action = KDualAction(i18n("Show tags"),
                                              i18n("Hide tags"), self)
        self.tag_display_action.setIconForStates(KIcon("image-x-generic"))
        self.tag_display_action.setEnabled(False)

        # Shortcuts
        connect_default = KAction.ShortcutTypes(KAction.DefaultShortcut)
        connect_active = KAction.ShortcutTypes(KAction.ActiveShortcut)

        self.connect_action.setShortcut(KStandardShortcut.open())
        self.fetch_action.setShortcut(KStandardShortcut.find())

        self.fetch_action.setEnabled(False)
        self.batch_download_action.setEnabled(False)
        self.pool_toggle_action.setEnabled(False)

    def setup_action_collection(self):
        """Set up the action collection by adding the actions."""

        action_collection = self.actionCollection()

        # Addition to the action collection
        action_collection.addAction("connect", self.connect_action)
        action_collection.addAction("fetch", self.fetch_action)
        action_collection.addAction("clean", self.clean_action)
        action_collection.addAction("batchDownload",
                                    self.batch_download_action)
        action_collection.addAction("poolDownload", self.pool_toggle_action)
        action_collection.addAction("tagDisplay", self.tag_display_action)

        KStandardAction.quit(self.close, action_collection)
        KStandardAction.preferences(self.show_preferences, action_collection)

        action_collection.removeAction(
            action_collection.action("help_contents"))
        action_collection.actionHovered.connect(self.setup_action_tooltip)

    def setup_actions(self):
        """Set up the relevant actions, tooltips, and load the RC file."""

        self.create_actions()
        self.setup_tooltips()
        self.setup_action_collection()

        # Connect signals
        self.connect_action.triggered.connect(self.connect)
        self.fetch_action.triggered.connect(self.get_posts)
        self.clean_action.triggered.connect(self.clean_cache)
        self.batch_download_action.triggered.connect(self.batch_download)
        self.pool_toggle_action.toggled.connect(self.pool_toggle)
        self.tag_display_action.activeChanged.connect(self.tag_display)

        window_options = self.StandardWindowOption(self.ToolBar | self.Keys
                                                   | self.Create | self.Save
                                                   | self.StatusBar)

        setupGUI_args = [
            QSize(500, 400),
            self.StandardWindowOption(window_options)
        ]

        #Check first in standard locations for danbooruui.rc

        rc_file = KStandardDirs.locate("appdata", "danbooruui.rc")

        if rc_file.isEmpty():
            setupGUI_args.append(os.path.join(sys.path[0], "danbooruui.rc"))
        else:
            setupGUI_args.append(rc_file)

        self.setupGUI(*setupGUI_args)

    def setup_action_tooltip(self, action):

        "Show statusbar help when actions are hovered."

        if action.isEnabled():
            self.statusBar().showMessage(action.toolTip(), 2000)

    def setup_connections(self):
        """Set up connections for post and tag retrieval."""

        if self.api is None:
            return

        self.api.postRetrieved.connect(self.update_progress)
        self.api.postDownloadFinished.connect(self.download_finished)
        self.api.tagRetrieved.connect(self.tag_dock.widget().add_tags)
        self.tag_dock.widget().itemDoubleClicked.connect(
            self.fetch_tagged_items)

    def show_preferences(self):

        "Show the preferences dialog."

        if KConfigDialog.showDialog("Preferences dialog"):
            return
        else:
            dialog = preferences.PreferencesDialog(self, "Preferences dialog",
                                                   self.preferences)
            dialog.show()
            dialog.settingsChanged.connect(self.reload_config)

    def connect(self, ok):

        "Connect to a Danbooru board."

        if self.thumbnailarea is None:
            self.first_fetch_widget.show()
        else:
            self.thumbnailarea.connectwidget.show()

    def restore(self):

        self.statusbar.removeWidget(self.connect_widget)

    def handle_connection(self, connection):

        self.api = None
        self.api = connection
        self.api.cache = self.cache

        if self.pool_dock is not None:
            self.pool_dock.hide()
            self.pool_dock.widget().clear()
            self.pool_toggle_action.setChecked(False)

        if self.thumbnailarea is not None:
            #TODO: Investigate usability
            self.clear(clear_pool=True)
            self.thumbnailarea.clear()
            self.thumbnailarea.api_data = self.api
            self.setup_connections()

        else:
            self.first_fetch_widget.connectionEstablished.disconnect()
            self.first_fetch_widget.rejected.disconnect()
            self.statusbar.removeWidget(self.first_fetch_widget)
            self.setup_area()

        self.api.cache = self.cache

        self.statusBar().showMessage(i18n("Connected to %s" % self.api.url),
                                     3000)
        self.fetch_action.setEnabled(True)

        # Set up pool widget

        pool_widget = poolwidget.DanbooruPoolWidget(self.api)
        self.pool_dock = QDockWidget("Pools", self)
        self.pool_dock.setObjectName("PoolDock")
        self.pool_dock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.pool_dock.setWidget(pool_widget)
        #self.pool_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.pool_dock)
        self.pool_dock.widget().poolDownloadRequested.connect(
            self.pool_prepare)
        self.pool_dock.hide()

        self.pool_toggle_action.setEnabled(True)
        self.clear()  # Needed to show properly the stuff after connecting

        self.api.get_post_list(tags="",
                               limit=self.thumbnailarea.post_limit,
                               rating=self.preferences.max_allowed_rating,
                               blacklist=list(self.preferences.tag_blacklist))
        self.api.get_tag_list(name="",
                              blacklist=list(self.preferences.tag_blacklist),
                              limit=20)

    def get_posts(self, ok):

        "Get posts from the connected Danbooru board."

        if not self.api:
            return

        self.thumbnailarea.fetchwidget.show()

    def handle_fetching(self, tags, max_rating, limit):
        """Slot connected to the dataSent signal of the fetch widget.

        The widgets are set up if they don't exist, and the API is queried
        to do the actual downloading of tags and

        """

        self.clear()

        self.thumbnailarea.fetchwidget.hide()

        if self.tag_dock is not None:
            self.tag_dock.widget().clear()

        self.thumbnailarea.post_limit = limit
        blacklist = list(self.preferences.tag_blacklist)
        self.api.get_post_list(tags=tags,
                               limit=limit,
                               rating=max_rating,
                               blacklist=blacklist)

        tags = [item for item in tags if item]

        if not tags:
            # No related tags, fetch the most recent 20
            tags = ""
            self.api.get_tag_list(name=tags, blacklist=blacklist, limit=20)
        else:
            self.api.get_related_tags(tags=tags, blacklist=blacklist)

    def fetch_tagged_items(self, item):
        """Fetch items found in the tag list widget."""

        tag_name = unicode(item.text())
        self.clear()

        blacklist = self.preferences.tag_blacklist
        limit = self.preferences.thumbnail_no
        rating = self.preferences.max_allowed_rating

        self.api.get_post_list(page=1,
                               tags=[tag_name],
                               blacklist=blacklist,
                               limit=limit,
                               rating=rating)
        self.api.get_related_tags(tags=[tag_name], blacklist=blacklist)

    def pool_toggle(self, checked):

        "Toggle the presence/absence of the pool dock."

        if not self.api:
            return

        if not checked:
            self.pool_dock.hide()
        else:
            self.pool_dock.show()

    def pool_prepare(self, pool_id):
        """Prepare the central area for pool image loading."""

        if self.thumbnailarea is None:
            self.setup_area()
        else:
            self.clear(clear_pool=False)

        self.api.get_pool(pool_id,
                          blacklist=self.preferences.tag_blacklist,
                          rating=self.preferences.max_allowed_rating)

    def batch_download(self, ok):

        "Download images in batch."

        selected_items = self.thumbnailarea.selected_images()

        if not selected_items:
            return

        start_url = KUrl("kfiledialog:///danbooru")
        caption = i18n("Select a directory to save the images to")
        directory = KFileDialog.getExistingDirectoryUrl(
            start_url, self, caption)

        if directory.isEmpty():
            return

        for item in selected_items:

            file_url = item.url_label.url()
            tags = item.data.tags

            # Make a local copy to append paths as addPath works in-place
            destination = KUrl(directory)

            file_name = KUrl(file_url).fileName()
            destination.addPath(file_name)

            job = KIO.file_copy(KUrl(file_url), destination, -1)
            job.setProperty("tags", QVariant(tags))
            job.result.connect(self.batch_download_slot)

    def setup_area(self):

        "Set up the central widget to display thumbnails."

        self.thumbnailarea = thumbnailarea.DanbooruTabWidget(
            self.api, self.preferences, self.preferences.thumbnail_no, self)

        self.setCentralWidget(self.thumbnailarea)

        self.thumbnailarea.connectwidget.connectionEstablished.connect(
            self.handle_connection, type=Qt.UniqueConnection)
        self.thumbnailarea.connectwidget.rejected.connect(
            self.thumbnailarea.connectwidget.hide, type=Qt.UniqueConnection)

        self.thumbnailarea.fetchwidget.dataSent.connect(
            self.handle_fetching, type=Qt.UniqueConnection)
        self.thumbnailarea.fetchwidget.rejected.connect(
            self.thumbnailarea.fetchwidget.hide, type=Qt.UniqueConnection)

        # Set up tag widget

        blacklist = self.preferences.tag_blacklist
        tag_widget = tagwidget.DanbooruTagWidget(blacklist, self)
        self.tag_display_action.setActive(True)
        self.tag_display_action.setEnabled(True)
        self.tag_dock = QDockWidget("Similar tags", self)
        self.tag_dock.setObjectName("TagDock")
        self.tag_dock.setAllowedAreas(Qt.RightDockWidgetArea)
        self.tag_dock.setWidget(tag_widget)
        #self.tag_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.RightDockWidgetArea, self.tag_dock)
        self.tag_dock.hide()

        # Container signal-slot connections

        self.setup_connections()

    def download_finished(self):
        """Slot called when all the data has been completed. Clears the progress
        bar and resets it to 0."""

        if not self.batch_download_action.isEnabled():
            self.batch_download_action.setEnabled(True)

        self.__step = 0
        self.progress.hide()

    def update_progress(self):

        "Update the progress bar."

        if not self.progress.isVisible():
            self.progress.show()

        self.__step += 1
        self.progress.setValue(self.__step)

    def clear(self, clear_pool=True):

        "Clear the central widget."

        if self.thumbnailarea is None:
            return

        self.thumbnailarea.clear()
        self.tag_dock.widget().clear()
        if clear_pool:
            self.pool_dock.widget().clear()
        self.batch_download_action.setEnabled(False)

    def clean_cache(self):

        "Purge the thumbnail cache."

        self.cache.discard()
        self.statusBar().showMessage(i18n("Thumbnail cache cleared."))

    def batch_download_slot(self, job):
        """Slot called when doing batch download, for each file retrieved.

        If Nepomuk tagging is enabled, each file is tagged using the item's
        respective tags.

        """

        if job.error():
            job.ui().showErrorMessage()
        else:
            if self.preferences.nepomuk_enabled:
                tags = job.property("tags").toPyObject()
                #danbooru2nepomuk.tag_danbooru_item(job.destUrl().path(),
                #                                   tags)

    def tag_display(self, state):
        """Display or hide the tag dock."""

        if self.tag_dock is None:
            self.tag_display_action.setActive(False)
            return

        if state:
            self.tag_dock.show()
        else:
            self.tag_dock.hide()
class MainWindow(KXmlGuiWindow):

    "Class which displays the main Danbooru Client window."

    def __init__(self,  *args):

        "Initialize a new main window."

        super(MainWindow,  self).__init__(*args)
        self.cache = KPixmapCache("danbooru")
        self.preferences = preferences.Preferences()
        self.api = None
        self.__ratings = None
        self.__step = 0

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no

        self.statusbar = self.statusBar()
        self.progress = QProgressBar()
        self.thumbnailarea = None
        self.tag_dock = None
        self.pool_dock = None
        self.first_fetch_widget = None

        self.progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # FIXME: Hackish, but how to make it small otherwise?
        self.progress.setMinimumSize(100, 1)
        self.statusbar.addPermanentWidget(self.progress)
        self.progress.hide()

        self.setup_welcome_widget()
        self.setup_actions()

    def reload_config(self):

        """Reload configuration after a change"""

        urls = self.preferences.boards_list

        if self.first_fetch_widget is not None:
            self.first_fetch_widget.setup_urls(urls)

        if self.thumbnailarea is not None:

            max_thumbnail = self.preferences.thumbnail_no
            max_rating = self.preferences.max_allowed_rating

            self.thumbnailarea.fetchwidget.limit = max_thumbnail
            self.thumbnailarea.fetchwidget.rating = max_rating
            self.thumbnailarea.fetchwidget.update_values()

            self.thumbnailarea.connectwidget.setup_urls(urls)

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no




    def setup_welcome_widget(self):

        """Load the welcome widget at startup."""

        widget = QWidget()
        layout = QVBoxLayout()

        welcome = QLabel(parent=self)
        pix = QPixmap(KStandardDirs.locate("appdata","logo.png"))

        welcome.setPixmap(pix)
        welcome.setAlignment(Qt.AlignCenter)

        self.first_fetch_widget = connectwidget.ConnectWidget(
        self.preferences.boards_list, self)

        self.statusbar.addPermanentWidget(self.first_fetch_widget, 300)

        self.first_fetch_widget.connectionEstablished.connect(
            self.handle_connection)
        self.first_fetch_widget.rejected.connect(
            self.first_fetch_widget.hide)

        self.first_fetch_widget.hide()

        self.first_fetch_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)

        layout.addWidget(self.first_fetch_widget)
        layout.addWidget(welcome)
        widget.setLayout(layout)

        self.setCentralWidget(widget)

    def setup_tooltips(self):

        """Set tooltips for the actions."""

        self.connect_action.setToolTip(i18n("Connect to a Danbooru board"))
        self.fetch_action.setToolTip(
            i18n("Fetch thumbnails from a Danbooru board")
        )
        self.batch_download_action.setToolTip(i18n("Batch download images"))

    def create_actions(self):

        """Create actions for the main window."""

        self.connect_action = KAction(KIcon("document-open-remote"),
                                 i18n("Connect"), self)
        self.fetch_action = KAction(KIcon("download"), i18n("Download"),
                                    self)
        self.clean_action = KAction(KIcon("trash-empty"),
                               i18n("Clear thumbnail cache"),
                               self)
        self.batch_download_action = KAction(KIcon("download"),
                                             i18n("Batch download"), self)
        self.pool_toggle_action = KToggleAction(KIcon("image-x-generic"),
                                            i18n("Pools"), self)
        self.tag_display_action = KDualAction(i18n("Show tags"),
                                              i18n("Hide tags"),
                                              self)
        self.tag_display_action.setIconForStates(KIcon("image-x-generic"))
        self.tag_display_action.setEnabled(False)

        # Shortcuts
        connect_default = KAction.ShortcutTypes(KAction.DefaultShortcut)
        connect_active = KAction.ShortcutTypes(KAction.ActiveShortcut)

        self.connect_action.setShortcut(KStandardShortcut.open())
        self.fetch_action.setShortcut(KStandardShortcut.find())

        self.fetch_action.setEnabled(False)
        self.batch_download_action.setEnabled(False)
        self.pool_toggle_action.setEnabled(False)

    def setup_action_collection(self):

        """Set up the action collection by adding the actions."""

        action_collection = self.actionCollection()

        # Addition to the action collection
        action_collection.addAction("connect", self.connect_action)
        action_collection.addAction("fetch", self.fetch_action)
        action_collection.addAction("clean", self.clean_action)
        action_collection.addAction("batchDownload", self.batch_download_action)
        action_collection.addAction("poolDownload", self.pool_toggle_action)
        action_collection.addAction("tagDisplay", self.tag_display_action)

        KStandardAction.quit (self.close, action_collection)
        KStandardAction.preferences(self.show_preferences,
                                    action_collection)

        action_collection.removeAction(
            action_collection.action("help_contents"))
        action_collection.actionHovered.connect(self.setup_action_tooltip)

    def setup_actions(self):

        """Set up the relevant actions, tooltips, and load the RC file."""

        self.create_actions()
        self.setup_tooltips()
        self.setup_action_collection()

        # Connect signals
        self.connect_action.triggered.connect(self.connect)
        self.fetch_action.triggered.connect(self.get_posts)
        self.clean_action.triggered.connect(self.clean_cache)
        self.batch_download_action.triggered.connect(self.batch_download)
        self.pool_toggle_action.toggled.connect(self.pool_toggle)
        self.tag_display_action.activeChanged.connect(self.tag_display)

        window_options = self.StandardWindowOption(self.ToolBar| self.Keys |
                                                   self.Create | self.Save |
                                                   self.StatusBar)

        setupGUI_args = [
            QSize(500, 400), self.StandardWindowOption(window_options)
        ]

        #Check first in standard locations for danbooruui.rc

        rc_file = KStandardDirs.locate("appdata", "danbooruui.rc")

        if rc_file.isEmpty():
            setupGUI_args.append(os.path.join(sys.path [0],
                                              "danbooruui.rc"))
        else:
            setupGUI_args.append(rc_file)

        self.setupGUI(*setupGUI_args)

    def setup_action_tooltip(self, action):

        "Show statusbar help when actions are hovered."

        if action.isEnabled():
            self.statusBar().showMessage(action.toolTip(), 2000)

    def setup_connections(self):

        """Set up connections for post and tag retrieval."""

        if self.api is None:
            return

        self.api.postRetrieved.connect(self.update_progress)
        self.api.postDownloadFinished.connect(self.download_finished)
        self.api.tagRetrieved.connect(self.tag_dock.widget().add_tags)
        self.tag_dock.widget().itemDoubleClicked.connect(
            self.fetch_tagged_items)

    def show_preferences(self):

        "Show the preferences dialog."

        if KConfigDialog.showDialog("Preferences dialog"):
            return
        else:
            dialog = preferences.PreferencesDialog(self, "Preferences dialog",
                                                   self.preferences)
            dialog.show()
            dialog.settingsChanged.connect(self.reload_config)

    def connect(self, ok):

        "Connect to a Danbooru board."

        if self.thumbnailarea is None:
            self.first_fetch_widget.show()
        else:
            self.thumbnailarea.connectwidget.show()


    def restore(self):

        self.statusbar.removeWidget(self.connect_widget)

    def handle_connection(self, connection):

        self.api = None
        self.api = connection
        self.api.cache = self.cache

        if self.pool_dock is not None:
            self.pool_dock.hide()
            self.pool_dock.widget().clear()
            self.pool_toggle_action.setChecked(False)

        if self.thumbnailarea is not None:
            #TODO: Investigate usability
            self.clear(clear_pool=True)
            self.thumbnailarea.clear()
            self.thumbnailarea.api_data = self.api
            self.setup_connections()

        else:
            self.first_fetch_widget.connectionEstablished.disconnect()
            self.first_fetch_widget.rejected.disconnect()
            self.statusbar.removeWidget(self.first_fetch_widget)
            self.setup_area()

        self.api.cache = self.cache

        self.statusBar().showMessage(i18n("Connected to %s" % self.api.url),
                                     3000)
        self.fetch_action.setEnabled(True)

        # Set up pool widget

        pool_widget = poolwidget.DanbooruPoolWidget(self.api)
        self.pool_dock = QDockWidget("Pools", self)
        self.pool_dock.setObjectName("PoolDock")
        self.pool_dock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.pool_dock.setWidget(pool_widget)
        #self.pool_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.pool_dock)
        self.pool_dock.widget().poolDownloadRequested.connect(
            self.pool_prepare)
        self.pool_dock.hide()

        self.pool_toggle_action.setEnabled(True)
        self.clear() # Needed to show properly the stuff after connecting

        self.api.get_post_list(tags="", limit=self.thumbnailarea.post_limit,
                               rating=self.preferences.max_allowed_rating,
                               blacklist=list(self.preferences.tag_blacklist))
        self.api.get_tag_list(name="",blacklist=list(self.preferences.tag_blacklist),
                              limit=20)

    def get_posts(self, ok):

        "Get posts from the connected Danbooru board."

        if not self.api:
            return

        self.thumbnailarea.fetchwidget.show()



    def handle_fetching(self, tags, max_rating, limit):

        """Slot connected to the dataSent signal of the fetch widget.

        The widgets are set up if they don't exist, and the API is queried
        to do the actual downloading of tags and

        """

        self.clear()

        self.thumbnailarea.fetchwidget.hide()

        if self.tag_dock is not None:
            self.tag_dock.widget().clear()

        self.thumbnailarea.post_limit = limit
        blacklist= list(self.preferences.tag_blacklist)
        self.api.get_post_list(tags=tags, limit=limit,
                               rating=max_rating,
                               blacklist=blacklist)

        tags = [item for item in tags if item]

        if not tags:
            # No related tags, fetch the most recent 20
            tags = ""
            self.api.get_tag_list(name=tags,blacklist=blacklist,
                                  limit=20)
        else:
            self.api.get_related_tags(tags=tags, blacklist=blacklist)

    def fetch_tagged_items(self, item):

        """Fetch items found in the tag list widget."""

        tag_name = unicode(item.text())
        self.clear()

        blacklist = self.preferences.tag_blacklist
        limit = self.preferences.thumbnail_no
        rating = self.preferences.max_allowed_rating

        self.api.get_post_list(page=1, tags=[tag_name],
                                blacklist=blacklist,
                                limit=limit,
                                rating=rating)
        self.api.get_related_tags(tags=[tag_name], blacklist=blacklist)

    def pool_toggle(self, checked):

        "Toggle the presence/absence of the pool dock."

        if not self.api:
            return

        if not checked:
            self.pool_dock.hide()
        else:
            self.pool_dock.show()

    def pool_prepare(self, pool_id):

        """Prepare the central area for pool image loading."""

        if self.thumbnailarea is None:
            self.setup_area()
        else:
            self.clear(clear_pool=False)

        self.api.get_pool(pool_id, blacklist=self.preferences.tag_blacklist,
                          rating=self.preferences.max_allowed_rating)

    def batch_download(self, ok):

        "Download images in batch."

        selected_items = self.thumbnailarea.selected_images()

        if not selected_items:
            return

        start_url = KUrl("kfiledialog:///danbooru")
        caption = i18n("Select a directory to save the images to")
        directory = KFileDialog.getExistingDirectoryUrl(start_url, self,
                                                        caption)

        if directory.isEmpty():
            return

        for item in selected_items:

            file_url = item.url_label.url()
            tags = item.data.tags

            # Make a local copy to append paths as addPath works in-place
            destination = KUrl(directory)

            file_name = KUrl(file_url).fileName()
            destination.addPath(file_name)

            job = KIO.file_copy(KUrl(file_url), destination, -1)
            job.setProperty("tags", QVariant(tags))
            job.result.connect(self.batch_download_slot)

    def setup_area(self):

        "Set up the central widget to display thumbnails."

        self.thumbnailarea = thumbnailarea.DanbooruTabWidget(self.api,
            self.preferences, self.preferences.thumbnail_no, self)

        self.setCentralWidget(self.thumbnailarea)

        self.thumbnailarea.connectwidget.connectionEstablished.connect(
                    self.handle_connection, type=Qt.UniqueConnection)
        self.thumbnailarea.connectwidget.rejected.connect(
            self.thumbnailarea.connectwidget.hide, type=Qt.UniqueConnection)

        self.thumbnailarea.fetchwidget.dataSent.connect(
            self.handle_fetching, type=Qt.UniqueConnection)
        self.thumbnailarea.fetchwidget.rejected.connect(
            self.thumbnailarea.fetchwidget.hide, type=Qt.UniqueConnection)

        # Set up tag widget

        blacklist = self.preferences.tag_blacklist
        tag_widget = tagwidget.DanbooruTagWidget(blacklist, self)
        self.tag_display_action.setActive(True)
        self.tag_display_action.setEnabled(True)
        self.tag_dock = QDockWidget("Similar tags", self)
        self.tag_dock.setObjectName("TagDock")
        self.tag_dock.setAllowedAreas(Qt.RightDockWidgetArea)
        self.tag_dock.setWidget(tag_widget)
        #self.tag_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.RightDockWidgetArea, self.tag_dock)
        self.tag_dock.hide()

        # Container signal-slot connections

        self.setup_connections()

    def download_finished(self):

        """Slot called when all the data has been completed. Clears the progress
        bar and resets it to 0."""

        if not self.batch_download_action.isEnabled():
            self.batch_download_action.setEnabled(True)

        self.__step = 0
        self.progress.hide()

    def update_progress(self):

        "Update the progress bar."

        if not self.progress.isVisible():
            self.progress.show()

        self.__step += 1
        self.progress.setValue(self.__step)

    def clear(self, clear_pool=True):

        "Clear the central widget."

        if self.thumbnailarea is None:
            return

        self.thumbnailarea.clear()
        self.tag_dock.widget().clear()
        if clear_pool:
            self.pool_dock.widget().clear()
        self.batch_download_action.setEnabled(False)

    def clean_cache(self):

        "Purge the thumbnail cache."

        self.cache.discard()
        self.statusBar().showMessage(i18n("Thumbnail cache cleared."))

    def batch_download_slot(self, job):

        """Slot called when doing batch download, for each file retrieved.

        If Nepomuk tagging is enabled, each file is tagged using the item's
        respective tags.

        """

        if job.error():
            job.ui().showErrorMessage()
        else:
            if self.preferences.nepomuk_enabled:
                tags = job.property("tags").toPyObject()
                #danbooru2nepomuk.tag_danbooru_item(job.destUrl().path(),
                #                                   tags)

    def tag_display(self, state):

        """Display or hide the tag dock."""

        if self.tag_dock is None:
            self.tag_display_action.setActive(False)
            return

        if state:
            self.tag_dock.show()
        else:
            self.tag_dock.hide()