def reload(self):

        # sanity checks
        if not self.sanityChecks(self.open_project):
            return

        # clear the widgets
        self.browser.clear()
        self.finder.clear()

        # parse the ignore list (used further down the call stack)
        self.ignore_list = self.get_option("ignore")
        if self.ignore_list:
            self.ignore_list = [x.strip() for x in self.ignore_list.split(",")]
        else:
            self.ignore_list = []

        # init the dir watcher
        self.dir_watcher = KDirWatch()
        QObject.connect(self.dir_watcher, SIGNAL("dirty ( const QString & )"), self.dirDirtied)
        QObject.connect(self.dir_watcher, SIGNAL("deleted ( const QString & )"), self.dirRemoved)

        # time and output the building of the tree
        t1 = time()
        self.addItem(QFileInfo(self.open_project), self.browser)
        kate.debug("project (re)load took %f seconds" % (time() - t1))
class DirectoryProject:
    def __init__(self, tool_widget):

        # we have to init the config first because all the widget __init__ methods below need it too
        self.initConfig()

        # all of our "child" widget
        self.browser = DPBrowser(tool_widget)
        self.finder = DPFinder(self)
        self.settings = DPSettings(self)

        self.dir_watcher = None
        self.open_project = None

        QObject.connect(self.browser, SIGNAL("doubleClicked ( QListViewItem *, const QPoint &, int )"), self.openItem)
        QObject.connect(
            self.finder.list_view, SIGNAL("doubleClicked ( QListViewItem *, const QPoint &, int )"), self.openItem
        )

        self.initMenu()

        last_project_path = self.config.get("general", "last")
        if last_project_path:
            self.openProject(last_project_path)

    def initMenu(self):
        # ugh, we gotta get a KActionCollection instance somehow
        action_collection = kate.sip.cast(kate.mainWidget().topLevelWidget(), KMainWindow).actionCollection()

        # because our KPopupMenu has no parent, we have to keep it from going out of scope
        self.menu = KPopupMenu()

        # Open
        action = KAction("&Open...", "fileopen", KShortcut("Ctrl+Shift+O"), self.menuOpen, action_collection)
        action.plug(self.menu)

        # Close
        action = KAction("&Close", "fileclose", KShortcut.null(), self.menuClose, action_collection)
        action.plug(self.menu)

        # Reload
        action = KAction("&Reload", "reload", KShortcut("Shift+F5"), self.menuReload, action_collection)
        action.plug(self.menu)

        self.menu.insertSeparator()

        # Find Files
        action = KAction("&Find Files...", "find", KShortcut("Ctrl+H"), self.menuFindFiles, action_collection)
        action.plug(self.menu)

        self.menu.insertSeparator()

        # Settings
        action = KAction("&Settings...", "configure", KShortcut.null(), self.menuSettings, action_collection)
        action.plug(self.menu)

        # insert the menu into the menu bar
        menu_bar = kate.mainWidget().topLevelWidget().menuBar()
        menu_bar.insertItem("&Project", self.menu, -1, 4)

    def initConfig(self):
        kate.debug("initConfig()")
        config_path = kate.pate.pluginDirectories[1] + "/%s/%s.conf" % (__name__, __name__)
        config_file = QFileInfo(config_path)

        if not config_file.exists():
            open(config_path, "w").close()

        config = ConfigParser()
        config.read(config_path)

        # init the DEFAULT options if they don't exist
        # the DEFAULT section is special and doesn't need to be created: if not config.has_section('DEFAULT'): config.add_section('DEFAULT')
        if not config.has_option("DEFAULT", "ignore"):
            config.set("DEFAULT", "ignore", "")
        if not config.has_option("DEFAULT", "filter"):
            config.set("DEFAULT", "filter", "*")
        if not config.has_option("DEFAULT", "finder_size"):
            config.set("DEFAULT", "finder_size", "400x450")
        if not config.has_option("DEFAULT", "config_size"):
            config.set("DEFAULT", "config_size", "300x350")
        if not config.has_option("DEFAULT", "search_type"):
            config.set("DEFAULT", "search_type", "word")

        # create the general section if it doesn't exist
        if not config.has_section("general"):
            config.add_section("general")

        # flush the config file
        config.write(open(config_path, "w"))

        # save the config object and config path as instance vars for use later
        self.config = config
        self.config_path = config_path

    # end def initConfig()

    # convenience method to get a config option for the currently open project
    def get_option(self, option_name):
        return self.config.get(self.open_project, option_name)

    # convenience method that sets a config option for the currently open project
    def set_option(self, name, value):
        self.config.set(self.open_project, name, value)

    # convenience method to save the config file
    def saveConfig(self):
        self.config.write(open(self.config_path, "w"))

    def reload(self):

        # sanity checks
        if not self.sanityChecks(self.open_project):
            return

        # clear the widgets
        self.browser.clear()
        self.finder.clear()

        # parse the ignore list (used further down the call stack)
        self.ignore_list = self.get_option("ignore")
        if self.ignore_list:
            self.ignore_list = [x.strip() for x in self.ignore_list.split(",")]
        else:
            self.ignore_list = []

        # init the dir watcher
        self.dir_watcher = KDirWatch()
        QObject.connect(self.dir_watcher, SIGNAL("dirty ( const QString & )"), self.dirDirtied)
        QObject.connect(self.dir_watcher, SIGNAL("deleted ( const QString & )"), self.dirRemoved)

        # time and output the building of the tree
        t1 = time()
        self.addItem(QFileInfo(self.open_project), self.browser)
        kate.debug("project (re)load took %f seconds" % (time() - t1))

    # end def reload()

    def openProject(self, project_path):

        # clean up the input
        project_path = str(project_path).strip()

        # sanity checks
        if not self.sanityChecks(project_path):
            return

        # little debug info
        kate.debug("opening new project: " + project_path)

        # set the open project
        self.open_project = project_path

        # init its config section if need be
        if not self.config.has_section(project_path):
            self.config.add_section(project_path)

        # update the last open project in the config section
        self.config.set("general", "last", project_path)

        # write the config so we always stay in sync
        self.saveConfig()

        # build the project tree!
        self.reload()

    # end def openProject()

    def passIgnore(self, file_info):
        file_name = file_info.fileName()
        if file_name == ".":
            return False
        if file_name == "..":
            return False
        if file_name in self.ignore_list:
            return False
        return True

    # end def passIgnore()

    def sanityChecks(self, project_path):
        file_info = QFileInfo(project_path)
        if not file_info.exists():
            kate.debug("project dir does not exist: %s" % project_path)
            return False
        if not file_info.isDir():
            kate.debug("project dir is not a directory: %s" % project_path)
            return False
        return True

    # end def sanityChecks()

    def openItem(self, item, trash1, trash2):
        self.openItems((item,))

    def openItems(self, items):
        for item in items:
            if not item:
                continue  # what the hell?  something is broken
            if item.is_dir == False:
                kate.debug("file dbl clicked")
                kate.documentManager.open(item.path)
                d = kate.documentManager.get(item.path)
                kate.application.activeMainWindow().viewManager().activateView(d.number)
                self.finder.close()
            else:
                kate.debug("dir dbl clicked")
                self.browser.setOpen(item, not self.browser.isOpen(item))

    # we only care about files and added dirs here.
    def dirDirtied(self, path):
        path = str(path)
        kate.debug("dirDirtied: " + path)

        # find the list view item for the watched directory
        lvi = self.browser.findItem(path, 1)
        if not lvi:
            kate.debug("cannot find directory: " + path)
            return

        # create a set of all it's children
        our_set = set()
        p = lvi.firstChild()
        while p:
            our_set.add(str(p.text(1)))
            p = p.nextSibling()

        # create a set of all the actual directory's children
        real_set = set()
        d = QDir(path)
        for file_info in d.entryInfoList():
            if file_info.fileName() == ".":
                continue
            if file_info.fileName() == "..":
                continue
            real_set.add(str(file_info.absFilePath()))

        # difference the set and those are our adds
        set_diff = real_set.difference(our_set)
        for path in set_diff:
            file_info = QFileInfo(path)
            self.addItem(file_info, lvi)
        kate.debug("added files/dirs: " + str(set_diff))

        # differece the other way and those are our deletes
        set_diff = our_set.difference(real_set)
        kate.debug("removing files/dirs: " + str(set_diff))
        for path in set_diff:
            self.removeItem(path)

    # we only care about removed dirs here.
    def dirRemoved(self, path):
        kate.debug("dirRemoved: " + str(path))

    # unlike browser and finder's addItem(), this is recursive
    def addItem(self, file_info, parent):

        # base case (file)
        if file_info.isFile():
            if self.passIgnore(file_info):
                p = self.browser.addItem(file_info, parent)
                self.finder.addItem(file_info, p.pixmap(0))

        # recursive case (dir)
        else:
            d = QDir(file_info.absFilePath())

            if not self.passIgnore(file_info):
                return

            parent = self.browser.addItem(file_info, parent)

            # watch this dir for changes
            self.dir_watcher.addDir(file_info.absFilePath())

            # do directories first
            d.setFilter(QDir.Dirs)
            for file_info in d.entryInfoList():
                self.addItem(file_info, parent)

            # now do files
            d.setFilter(QDir.Files)
            d.setNameFilter(self.config.get(self.open_project, "filter"))
            for file_info in d.entryInfoList():
                self.addItem(file_info, parent)

    # unlike browser and finder's removeItem(), this is recursive
    def removeItem(self, path):
        p = self.browser.findItem(path, 1)

        # base case (file)
        if p.childCount() == 0:
            self.browser.removeItem(path)
            self.finder.removeItem(path)

        # recursive case (dir)
        else:
            n = p.firstChild()
            while n:
                temp = n  # the ole linked list gotcha:  if we delete n, then n.nextSibling() will return None
                n = n.nextSibling()
                self.removeItem(temp.text(1))
            self.browser.removeItem(path)
            self.dir_watcher.removeDir(path)

    def menuOpen(self):
        kate.debug("menuOpen()")
        project_path = KFileDialog.getExistingDirectory()
        if project_path:
            self.openProject(project_path)

    def menuClose(self):
        kate.debug("menuClose()")
        self.browser.clear()
        self.finder.clear()
        self.open_project = None
        self.config.set("general", "last", "")
        self.saveConfig()

    def menuFindFiles(self):
        kate.debug("menuFindFiles()")
        if not self.open_project:
            KMessageBox.information(kate.mainWidget(), "Open a project first.", "No Project Open")
        else:
            self.finder.show()

    def menuReload(self):
        kate.debug("menuReload()")
        if self.open_project:
            self.reload()
        else:
            KMessageBox.information(kate.mainWidget(), "There is no project open to reload.", "No Project Open")

    def menuSettings(self):
        kate.debug("menuSettings()")
        self.settings.show()