#!/usr/bin/env python
# coding=utf-8
#
# FileListApplet.py
# 
# Copyright © 2008 Steven Brown <steven.w.j.brown@gmail.com>
#
# This file is part of File List Applet.
#
# FileListApplet is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# FileListApplet is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with FileListApplet.  If not, see <http://www.gnu.org/licenses/>.

from GnomePanelApplet import GnomePanelApplet
from FileTypeList import FileTypeList

import sys, os, time
import subprocess
import pyinotify
import gtk, gnomeapplet
import gobject
import xdg.BaseDirectory

from urllib import unquote
from urlparse import urlparse

# Performance ------------------------------------------------------------
#TODO: Remove delay when clicking "Open" on the folder selection dialog? - requires threading
#TODO: Re-use 'GtkImage' to save space and time.  cache by type?

# Application Types ------------------------------------------------------------
#TODO: "Bubble up" highly populated Application subtypes: PDF, ODT, etc.  But how to organize them? extra menu on TYPE list?
#TODO: SOME form of organization to the "application" category of files.
#      - group "package/zip/compressed" types
#      - separating different groups?

# Features ------------------------------------------------------------
# These all require creating a new menu creation/management class PRIORITY
#TODO: **RECENT ITEMS** for all files menuitem 
#TODO: **SUMMARY** on main menu - collection of all files in all monitored directories
#TODO: **Delete Monitor** - no point until the new menu creation/management class is created
#TODO: Allow toggling sort method, from modtime to alphabetical? (LOW PRIORITY)

# Backend ------------------------------------------------------------
#TODO: Store values in gconf to restore monitored directories (LOW PRIORITY)


# GUI ------------------------------------------------------------
#TODO: Move away from UIManager.  Maybe it can solve the context menu issue. (disappearing hierarchy vs not closing on activation)
#TODO: Preferences Dialog - should implement gconf first
#TODO: If total items is small (<=10?), instead of creating submenus, split the "media" menu into sections with titles
#TODO: If number of items in a menu is less than MAX_MEDIUM_SIZE_ITEMS, then use small icons?  (LOW priority)
#TODO: Larger Icons on the root menu

# Error Handling ------------------------------------------------------------
#TODO: Fall back to a dialog prompt for file-types with no association?

# Drag and Drop ------------------------------------------------------------
#TODO: Drag and Drop, using menu as a source of URIs - but... create link, copy, or move?



# Done ------------------------------------------------------------


PROGRAM_NAME = "File List Applet"
VERSION = "pre 0.1"
LICENSE = """
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
COPYRIGHT = "Copyright © Steven Brown 2008"
AUTHORS = ["Steven Brown <steven.w.j.brown@gmail.com>", "and you? :)"]
COMMENTS = "Browse files of any folder by type and modification time."
WEBSITE = "http://www.stevenbrown.ca/blog/?p=497"

OPEN_CMD = None

devnull = open("/dev/null", "w")
for cmd in ["gnome-open", "kde-open", "exo-open", "xdg-open", None]:
    if cmd:
        try:
            subprocess.call(cmd, stdout = devnull, stderr = devnull)
            OPEN_CMD = cmd
            break
        except OSError:
            print "'%s' not found" % cmd
            #if last item, print message then sys.exit(1), else, continue
            continue
    else:
        print "No program found to open files with.  Exiting..."
        sys.exit(1)

devnull.close()
print "Found '%s' to open files with." % cmd #DEBUG


# setup trash folders... hopefully.  Could be more thorough.
TRASH_LOC = os.path.join(xdg.BaseDirectory.xdg_data_home, "Trash")
TRASH_FILES = os.path.join(TRASH_LOC, 'files')
TRASH_INFOS = os.path.join(TRASH_LOC, 'info')
if not os.path.exists(TRASH_INFOS):
    os.makedirs(TRASH_INFOS)
if not os.path.exists(TRASH_FILES):
    os.makedirs(TRASH_FILES)
    

def trash(filepath):
    "Send file to Trash with Restore capability."
    # determine destination name
    filename = os.path.basename(filepath)
    trash_filename = os.path.join(TRASH_FILES, filename)
    counter = 1

    # make sure it's unique (same method as nautilus, hopefully)
    while os.path.exists(trash_filename):
        fn,ext = os.path.splitext(trash_filename)
        trash_filename = fn + "." + str(counter) + ext
        counter += 1

    try:
        print "Trashing ", filepath, "as", trash_filename
        os.rename(filepath, trash_filename)
        
        trash_info_filename = os.path.join(TRASH_INFOS,
                                           os.path.basename(trash_filename) +
                                           '.trashinfo')
        f = open(trash_info_filename, 'w')
        print >>f, "[Trash Info]"
        print >>f, "Path=%s" % filepath
        print >>f, "DeletionDate=%s" % time.strftime("%Y-%m-%dT%H:%M:%S")
        f.close()

    except Exception, e:
        print "There was an error trashing file: ", filepath, e
    


# global callback for file activation
def on_activate_file(widget, data=None):
    print " on_file_activate!", data
    devnull = open("/dev/null", "w")
    p = subprocess.Popen((OPEN_CMD, data), stdout = devnull, stderr = devnull)
    devnull.close()

def on_root_item_rename(widget, data=None):
    print "RENAME", widget, data
    #data.root_menuitem.get_children()[0].set_label("TESTING")
    #TODO: implement with popup dialog
    #FIXME: pass GMenuFileListManager into this function

def on_root_item_remove(widget, data=None):
    print "Remove", widget, data
    data.uimanager.remove_ui(data.merge_id)
    #FIXME: pass GMenuFileListManager into this function, and properly remove 'data'...


def on_root_menuitem_context_menu(widget, event, gfilelist, pshell, pitem):
    print "on_root_menuitem_context_menu()"
    if event.button == 3:
        # print "IT's A RIGHT CLICK!!"
        menu = gtk.Menu()
        title = gtk.MenuItem("Manage")
        title.set_sensitive(False)
        menu.append(title)
        menu.append(gtk.SeparatorMenuItem())
        
        openroot = gtk.ImageMenuItem(gtk.STOCK_OPEN)
        openroot.connect("activate", on_activate_file, gfilelist.path)

        rename = gtk.ImageMenuItem(gtk.STOCK_EDIT)
        rename.get_children()[0].set_label("Change Label")
        rename.connect("activate", on_root_item_rename, gfilelist)
        rename.set_sensitive(False) # not implemented
        
        remove = gtk.ImageMenuItem(gtk.STOCK_REMOVE)
        remove.get_children()[0].set_label("Remove")
        remove.get_children()[0].set_use_underline(False)
        remove.connect("activate", on_root_item_remove, gfilelist)
        #delete.set_sensitive(False)

        #delete.connect("activate", on_delete_root, data)
        menu.append(openroot)
        menu.append(rename)
        menu.append(remove)
        cancel = gtk.ImageMenuItem(gtk.STOCK_CANCEL) # for now
        #sort.set_sensitive(False) # not implemented
        menu.append(cancel)
#        menu.attach_to_widget(pitem,menu.destroy)
        menu.show_all()
        
        menu.popup(pshell, pitem, None, event.button, event.time)
        return True #consume
    return False #propagate

def on_media_context_menu(widget, event, label, pshell, pitem):
    print "on_media_context_menu()"
    if event.button == 3:
        # print "IT's A RIGHT CLICK!!"

        menu = gtk.Menu()
        title = gtk.MenuItem(label)
        title.set_sensitive(False)
        menu.append(title)
        menu.append(gtk.SeparatorMenuItem())

        sort = gtk.MenuItem("Sort by Name (NA)")
        #sort.set_sensitive(False) # not implemented.. BUT can't get rid of it unless ESC over sensitive item
        menu.append(sort)

#        menu.attach_to_widget(pitem,menu.destroy)
        menu.show_all()

        menu.popup(pshell, pitem, None, event.button, event.time)
        return True #consume
    return False #propagate

def on_file_context_menu(widget, event, filepath, pshell, pitem):
    print "on_file_context_menu()"
    if event.button == 3:
        #print "IT's A RIGHT CLICK!!"
        menu = gtk.Menu()

        title = gtk.MenuItem("File")
        title.set_sensitive(False)
        menu.append(title)
        menu.append(gtk.SeparatorMenuItem())
        
        delete = gtk.ImageMenuItem(gtk.STOCK_DELETE)
        delete.connect("activate", on_delete_file, filepath)

        openfile = gtk.ImageMenuItem(gtk.STOCK_OPEN)
        openfile.connect("activate", on_activate_file, filepath)

        props = gtk.ImageMenuItem(gtk.STOCK_PROPERTIES)
        props.set_sensitive(False)

        menu.append(openfile)
        menu.append(delete)
        menu.append(props)

#        menu.attach_to_widget(pitem,menu.destroy)
        menu.show_all()

        menu.popup(pshell, pitem, None, event.button, event.time)
        return True
    return False

def on_delete_file(widget, filepath):
    trash(filepath)

iconcache = [] # hack for speedier icon searching

ui = """
<ui>
  <menubar name="MenuBar">
	<menu name="MonitorsMenu" action="Monitors">
	  <placeholder name="placeholder">
	  </placeholder>
	  <separator name="sep1"/>
	  <placeholder name="summary">
	  </placeholder>
	  <separator name="sep2"/>
	  <menuitem action="Add Monitor"/>
	</menu>
  </menubar>
</ui>
"""

ui_monitor_popup = """
<ui>
  <popup name="MonitorItemPopup">
    <menuitem action="Open Location"/>
    <separator name="sep1"/>
	<menuitem action="Refresh"/>
    <menuitem action="Remove"/>
  </popup>
</ui>
"""


uimanager = None


def get_icon(icon_theme, media, subtype=None, ext=None, size=24):
    #TODO: use extension, ext information, if given
    "Returns a gtk.Image with a best-guess mimetype icon according to media and subtype."
    image = gtk.Image()
    lookup = media

    global iconcache

    l = []
    if subtype:
        l = filter(lambda i: subtype in i, iconcache)
        #print subtype, "l: ", l  #DEBUG

    if len(l) > 1:
        lookup = l[0] #first element from list
        
    elif media == "audio":
        lookup = "sound"

    elif media == "inode":
        lookup = "folder"

    try:
        image.set_from_pixbuf(icon_theme.load_icon(lookup, size, 0))
    except:
        image.set_from_pixbuf(icon_theme.load_icon("empty", size, 0))

    return image


class ErrorMessageDialog(gtk.Dialog):
    """Simple Dialog with a Close button to relay messages.
    Takes a title and message as arguments."""
    def __init__(self, title, message, *args, **kwargs):
        super(ErrorMessageDialog, self).__init__(title,
                                                 buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE),
                                                 *args, **kwargs)
        self.vbox.pack_start(gtk.Label(message))
        self.vbox.show_all()
        self.action_area.show_all()

class GMenuFileList(FileTypeList):
    def __init__(self, path, uimanager, actiongroup):
        super(GMenuFileList, self).__init__(path)

        #GUI stuff
        # action for root menu item (Folder)
        label = os.path.basename(path)
        counter = 1
        while uimanager.get_widget('/MenuBar/MonitorsMenu/placeholder/'+label):
            label = os.path.basename(path) + str(counter)
            counter = counter + 1

        
        action_name = path
        action = gtk.Action(name = action_name,
                            label = label, # label
                            tooltip = "tooltip",
                            stock_id = gtk.STOCK_OPEN)
        #action.connect("activate", self.on_monitor_activate, path) #TODO - context menu
        actiongroup.add_action( action )

        # add the root menu item using the uimanagaer
        self.merge_id = uimanager.new_merge_id()
        uimanager.add_ui(self.merge_id, #merge_id to use
                              '/MenuBar/MonitorsMenu/placeholder', #path
                              label, #name
                              action_name, #action
                              gtk.UI_MANAGER_MENUITEM, False) #type, top
        uimanager.ensure_update()

        root_menuitem = uimanager.get_widget('/MenuBar/MonitorsMenu/placeholder/'+label)
        root_menu = uimanager.get_widget('/MenuBar/MonitorsMenu/').get_submenu()
#         print "root_menu: ", root_menu
#         print "item's parent: ", root_menuitem.get_parent()
        root_menuitem.get_children()[0].set_use_underline(False)
        root_menuitem.connect("button-press-event", on_root_menuitem_context_menu, self, root_menu, root_menuitem)
        self.label = label
        self.root_menu = root_menu
        self.root_menuitem = root_menuitem
        self.uimanager = uimanager
        self.action_name = action_name
        self.action = action
        self.actiongroup = actiongroup

        self.update_all_menus() # build menus from scratch

    def get_menuitem_by_label(self, label):
        #TODO
        pass

    def update_all_menus(self):
        print "update_all_menus" # debug
        try:
            self.media_menu = self.build_media_menu()
            self.root_menuitem.set_submenu(self.media_menu)
            self.root_menuitem.show_all()
        except:
            print "Exception Occurred!"
            pass # the object may not be fully constructed yet
        
    def add_file_gui(self, filepath):
        super(GMenuFileList, self).add_file(filepath)
        self.update_all_menus()
        print "done update_all_menus"

    def remove_file_gui(self, filepath):
        super(GMenuFileList, self).remove_file(filepath)
        self.update_all_menus()
        print "done update_all_menus"

    def update_file_gui(self, filepath):
        super(GMenuFileList, self).update_file(filepath)
        self.update_all_menus()
        print "done update_all_menus"


    def build_file_menu(self, m, icon_theme=None):
        "Returns a GtkMenu class with all files of type m.  l = FileTypeList, m=media (string)"
        filelist = gtk.Menu()
        if not icon_theme:
            icon_theme = gtk.icon_theme_get_default()

        # Build the file list
        for f in self.get_files_by_media(m):
            fileitem = gtk.ImageMenuItem(gtk.STOCK_OPEN, None)
            fileitem.get_children()[0].set_label("%s" % (f))
            fileitem.get_children()[0].set_use_underline(False)
            fileitem.set_image(get_icon(icon_theme, m, self.get_subtype(f)))
            fileitem.connect("activate", on_activate_file, os.path.join(self.path,f) )
            fileitem.connect("button-press-event", on_file_context_menu, os.path.join(self.path,f), self.root_menu, fileitem)
#            fileitem.connect("button-press-event", on_file_context_menu, os.path.join(self.path,f), filelist, fileitem)
#            fileitem.connect("button-press-event", on_file_context_menu, os.path.join(self.path,f), None, fileitem)            
            fileitem.set_tooltip_text( self.get_comment(f).title() )
            filelist.append(fileitem)

        return filelist


    def build_media_menu(self, menu=None, icon_theme=None):
        if not menu:
            menu = gtk.Menu()

        if not icon_theme:
            icon_theme = gtk.icon_theme_get_default()
            
        media_with_counts = self.get_media_counts()
        

        if len(media_with_counts) <= 0:
            description = gtk.MenuItem("No files",False)
            description.set_sensitive(False)
            menu.append(description)

        elif len(media_with_counts) == 1:
            menu = self.build_file_menu( media_with_counts[0][0], icon_theme)

        else:
            # separate folders from files
            if "inode" in zip(*media_with_counts)[0]:
                menu.prepend(gtk.SeparatorMenuItem())

            # Build the media list
            for m,c in media_with_counts:
                label = m
                if m == "inode":
                    label = "folder"

                subitem = gtk.ImageMenuItem(gtk.STOCK_OPEN, None)
                subitem.get_children()[0].set_label("%s (%i)" % (label.title(), c))

                subitem.set_image(get_icon(icon_theme, m))
                #Now populate the filelist for this media
                filelist = self.build_file_menu(m, icon_theme)
                subitem.connect("button-press-event", on_media_context_menu, label.title(), self.root_menu, subitem)
#                subitem.connect("button-press-event", on_media_context_menu, label.title(), None, subitem)

                filelist.show_all()
                subitem.set_submenu(filelist)
                st = self.get_subtypes(m)
                subitem.set_tooltip_text("%i Subtypes: %s" % (len(st), ", ".join(st).title() ))

                if label == "folder":
                    menu.prepend(subitem)
                else:
                    menu.append(subitem)

        menu.show_all()
        return menu


class GMenuFileListManager(object):
    def __init__(self):
        super(GMenuFileListManager, self).__init__()
        self.filelists = []

    def get_filelist_by_path(self, path):
        for t in self.filelists:
            if t[1].path == path:
                return t[1]

        # path not in filelists
        raise ValueError()

    def get_label_by_path(self, path):
        for t in self.filelists:
            if t[1].path == path:
                return t[0]

        # path not in filelists
        raise ValueError()

    def get_label_filelist_by_path(self, path):
        "Returns the tuple: (label, filelist)."
        for t in self.filelists:
            if t[1].path == path:
                return t

        # path not in filelists
        raise ValueError()


    def add(self, filelist):
        # append the (label, filelist) tuple to self.filelists
        self.filelists.append( (os.path.basename(filelist.path), filelist))
        #TODO: update GUI

    def remove(self, path):
        for t in self.filelists:
            if t[1].path == path:
                del t
                #TODO: update GUI

        # path not in filelists
        raise ValueError()

    def path_exists(self, path):
        for t in self.filelists:
            if t[1] == path:
                return True
        return False

    def change_label(self, path, label):
        t = self.get_label_filelist_by_path(path)
        t[0] = label
    
        
    

# + remove(l)
# + add(GMenuFileList l)
# + change_label(l)
# + update_summary()
# + get_filelist_by_index # 
# + get_filelist_by_path  # should be unique
# + get_filelist_by_label # returns first found
# + build_summary()
# + update_summary() # takes main menu as argument?
# [(label, GMenuFileList), (label, GMenuFileList) ... ]

## allow these formats: 
##   filelist = self.filelists[path]
##   if path in self.filelists ....



    
    

class FileListApplet(GnomePanelApplet):

    baiid = "OAFIID:FileListApplet_Factory"
    description = "Browse files of any folder by type and modification time"
    version = "0.0"
    
    def __init__(self, *args, **kwargs):
        #super(FileListApplet, self).__init__(*args, **kwargs)
        self.__gobject_init__()


    def default_action(self, dialog, link, user_data):
        print "default_action: ", dialog, link, user_data
        if user_data:
            link = user_data + link # add protocol, if specified.
        on_activate_file(dialog, link)

    def file_updated(self, filepath, action):
        "Updates the gui appropriately."
        path,filename = os.path.split(filepath)
        if self.filelists.path_exists(path):
            print "file '%s' not being monitored." % filepath # should never happen
            return

        print "file_updated() file=%s, action=%s" % (filepath, action) # debug
        filelist = self.filelists.get_filelist_by_path(path)
        if "create" in action:
            filelist.add_file_gui(filename)

        elif "modify" in action:
            filelist.update_file_gui(filename)

        elif "delete" in action:
            filelist.remove_file_gui(filename)
            
        #TODO - EFFICIENTLY update gui


    def remove_folder(self, path):
        #TODO
        pass

    def update_folder(self, path):
        #TODO
        pass
        
    def add_folder(self, path, recursive):
        "Adds path to list of paths to monitor."
        if os.path.exists(path):
            
            if self.filelists.path_exists(path):
                # This folder has already been added.
                print "This folder has already been added. Skipping."
                return

            l = GMenuFileList(path, self.uimanager, self.actiongroup)
            self.filelists.add(l)
            self.wm.add_watch(path, self.wm_mask, rec=recursive)

            #TODO: Add "Recently Modified >" section and separator

        else:
            #FIXME: properly handle error..
            print "'%s' is not a valid path." %path

    def remove_folder(self, path):
        "Removes an existing monitor."
        #TODO: implement
        pass
        
    def applet_factory(self, iid):
        # enclose Process Monitor class within applet_factory() method
        class PMon(pyinotify.ProcessEvent):
            def __init__(pmonself):
                pmonself.last_modified = None # absolute filename of last modified file
                pmonself.applet = self    # the applet for when updates are needed
                pmonself.last_gui_update = time.time() # time of last successful update_ui() call

            def update_ui(pmonself, path, action):
                if not pmonself.applet:
                    return

                # easy there, big fella....
                #   if (time.time() - pmonself.last_gui_update) < 0.5:
                #     return
                # TODO: Add to event QUEUE

                print "enter threads"
                gtk.gdk.threads_enter()
                pmonself.applet.file_updated(path, action)
                gtk.gdk.threads_leave()
                print "exit threads"
                pmonself.last_gui_update = time.time()

            def process_IN_CREATE(pmonself, event):
                print "CREATE: %s" % os.path.join(event.path, event.name)
                if event.name[0] == ".":
                    return
                pmonself.update_ui( os.path.join(event.path, event.name), "create")

            def process_IN_DELETE(pmonself, event):
                print "DELETE: %s" % os.path.join(event.path, event.name)
                if event.name[0] == ".":
                    return
                pmonself.update_ui( os.path.join(event.path, event.name), "delete")

            def process_IN_MODIFY(pmonself, event):
                absfile = os.path.join(event.path, event.name)

                print "MODIFY: %s" % absfile
                if event.name[0] == ".":
                    return

                if pmonself.last_modified and pmonself.last_modified == absfile:
                    return # no changes to sorting

                pmonself.update_ui( os.path.join(event.path, event.name), "modify")

                pmonself.last_modified = absfile

            def process_IN_MOVED_FROM(pmonself, event):
                print "MOVED FROM:"
                print "  "+event.name
                if event.name[0] == ".":
                    return
                pmonself.update_ui( os.path.join(event.path, event.name), "delete") #FIXME: kind of hackish

            def process_IN_MOVED_TO(pmonself, event):
                print "MOVED TO:"
                print "  "+event.name
                if event.name[0] == ".":
                    return
                pmonself.update_ui( os.path.join(event.path, event.name), "create") #FIXME: kind of hackish

            def process_default(pmonself, event):
                print "PROCESS DEFAULT EVENT:"
                print "  "+event.name
                if event.name[0] == ".":
                    return
                # custom processing for default event goes here


        # resume regular scheduled applet_factory() method....
                
        # inotify
        self.wm = pyinotify.WatchManager()
        self.wm_mask = pyinotify.EventsCodes.IN_DELETE | \
            pyinotify.EventsCodes.IN_CREATE | \
            pyinotify.EventsCodes.IN_MODIFY | \
            pyinotify.EventsCodes.IN_MOVED_FROM | \
            pyinotify.EventsCodes.IN_MOVED_TO
        self.notifier = pyinotify.ThreadedNotifier(self.wm, PMon())

        self.notifier.start()

        # folder selection dialog
        self.dialog_select_folder = gtk.FileChooserDialog("Select a Folder to Monitor...",
                                                          None,
                                                          gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
                                                          (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                                           gtk.STOCK_OPEN, gtk.RESPONSE_OK))

        self.dialog_select_folder.set_default_response(gtk.RESPONSE_OK)

        # about dialog
        gtk.about_dialog_set_url_hook(self.default_action, None)
        gtk.about_dialog_set_email_hook(self.default_action, "mailto:")

        self.dialog_about = gtk.AboutDialog()
        self.dialog_about.set_name(PROGRAM_NAME)
        self.dialog_about.set_version(VERSION)
        self.dialog_about.set_copyright(COPYRIGHT)
        self.dialog_about.set_license(LICENSE)
        self.dialog_about.set_authors(AUTHORS)
        self.dialog_about.set_comments(COMMENTS)
        self.dialog_about.set_website(WEBSITE)
        self.dialog_about.set_website_label("Homepage")
        self.dialog_about.set_logo_icon_name(gtk.STOCK_OPEN)

        # prefs dialog - placeholder
        self.prefs_dialog = ErrorMessageDialog("Not Yet Implemented", # title
                                               "Sorry... This hasn't been implemented yet.  :(") # message

        # dict of GMenuFileList objects, folder-paths are keys
        #self.filelists = {} #TODO: use GMenuFileListManager
        self.filelists = GMenuFileListManager()

        # icons
        global iconcache
        icon_theme = gtk.icon_theme_get_default()
        if not iconcache:
            iconcache = icon_theme.list_icons()

        gtk.window_set_default_icon_name(gtk.STOCK_OPEN)

        self.label = gtk.Label("No Message Yet")
        self.connect("destroy", self._on_applet_destroy)

        # Actions
        actiongroup = gtk.ActionGroup("RadioSortMethod")
        self.actiongroup = actiongroup

        
        actiongroup.add_radio_actions([('SortByModTime', None, 'Sort by Modification _Time (Recent First)', None, 'Sort by modification time', 0),
                                      ('SortByName', gtk.STOCK_SORT_ASCENDING, 'Sort by _Name', None, 'Sort by filename', 1)
                                       ], 0, self.on_sort_toggle) #default, callback

        actiongroup.add_actions([('Add Monitor', gtk.STOCK_ADD, '_Add Folder', None, 'Add New Monitor', self.on_add_folder),
                                 ('Monitors', gtk.STOCK_OPEN, 'Files'),
                                 ('Manage', gtk.STOCK_PREFERENCES, '_Manage'),
                                 ('RadioSortMethod', gtk.STOCK_SORT_ASCENDING, '') ])

        global label
        label = self.label
  
        #self.set_applet_flags(gnomeapplet.EXPAND_MINOR) #Causes border to appear

        global uimanager
        uimanager = gtk.UIManager()
        uimanager.insert_action_group(actiongroup, 0)
        uimanager.add_ui_from_string(ui)
        self.uimanager = uimanager

        self.menubar = uimanager.get_widget("ui/MenuBar")
        print "menubar: ", self.menubar
        #print "parent: ", self.menubar
        self.menubar.connect("button-press-event", self.on_menubar_press)

        # Drag'n Drop implementation
        TARGET_STRING, TARGET_ROOTWIN = 0,1
        targets = (
            ('STRING', 0, TARGET_STRING),
            ('text/plain', 0, TARGET_STRING),
            ('application/x-rootwin-drop', 0, TARGET_ROOTWIN)
            )
        self.drag_dest_set(gtk.DEST_DEFAULT_ALL, targets,
                             gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
        self.connect('drag_data_received', self.on_applet_drag_data_received)


        # Remove gaps
#         gtk.rc_parse_string('''
#             style "applet-style"
#             {
#                     GtkWidget::focus-line-width = 0
#                     GtkWidget::inner-border = 0
#                     GtkWidget::internal-padding = 0
#                     GtkWidget::focus-padding = 0
#                     GtkWidget::shadow-type = none
#                     bg[NORMAL] = "#006600"
#             }
#             style "item-style"
#             {
#                     bg[NORMAL] = "#660000"
#             }
#             #class "*Applet*" style "applet-style"
#             class "*GtkMenuBar*MenuItem*" style "item-style"
#             class "*MenuBar*"  style "applet-style"''')


        gtk.rc_parse_string('''
		style "menubar-applet-style"
		{
            GtkWidget::focus-padding = 0
            GtkWidget::focus-line-width = 5
            GtkWidget::interior-focus = 1

            #GtkWidget::focus-line-pattern = none
			#GtkMenuBar::shadow-type = 0
			#GtkMenuItem::horizontal-padding = 3

			#GtkMenuBar::internal-padding = 0
			#GtkWidget::inner-border = 0
		}
		widget "*.gimmesomestyle" style "menubar-applet-style"''')

        self.menubar.set_name("gimmesomestyle")
        self.menubar.connect("size-allocate", self.on_menubar_size_allocate)

        context_menu_xml = """
		<popup name="button3">
		  <menuitem name="Item 1" verb="Prefs" label="_Preferences..." pixtype="stock" pixname="gtk-properties"/>
		  <menuitem name="Item 2" verb="About" label="_About..." pixtype="stock" pixname="gnome-stock-about"/>
		</popup>
		"""

        self.setup_menu(context_menu_xml, [("Prefs", self.on_prefs), ("About", self.on_about)], None)
        self.add(self.menubar)
        self.show_all()

        return True             # return applet_factory()

    
    
    def on_prefs(self, bbuicomponent, data=None):
        print "on_prefs!!"
        r = self.prefs_dialog.run()
        print "result:", r
        self.prefs_dialog.hide()
        
    def on_about(self, bbuicomponent, data=None):
        response = self.dialog_about.run()
        if response in (gtk.RESPONSE_CLOSE, gtk.RESPONSE_CANCEL):
            pass
        self.dialog_about.hide()
        
        
    def on_menubar_press(self, widget, event):
        if event.button != 1:
            #print "right or middle"
            #return True # Consume event? #<--- Doesn't work
            widget.emit_stop_by_name("button-press-event")  # Must emit_stop_by_name
            #return False
        #print "left"
        return False # Propagate event

    # This little hack is required to get Fitt's Law compliance -
    # i.e. to get the Menubar to be the full height of the panel.
    # THANK YOU, browser-bookmarks-menu applet.  ;)
    def on_menubar_size_allocate(self, menubar, rect):
        if (rect.x <= 0) or (rect.y <= 0):
            return False
        rect.x -= 1
        rect.y -= 1
        rect.width  += 2
        rect.height += 2
        gtk.Widget.size_allocate(menubar, rect)
        return False

    def on_applet_drag_data_received(self, w, context, x, y, data, info, time):
        if data and data.format == 8:
            #data is a GtkSelectionData object

            path = urlparse(data.data)[2]
            path = unquote(path).strip()

            if os.path.exists(path) and os.path.isdir(path):
                self.add_folder(path, False)

            context.finish(True, False, time)  #successful drop at time, don't delete source
        else:
            if data:
                print "dropped data not recognized: "
                print "  get_text:", data.get_text()
                print "  get_targets:", data.get_targets()
                print "  get_uris:", data.get_uris()
                print "  targets_include_text()", data.targets_include_text()
                print "  targets_include_image()", data.targets_include_image()
                print "  targets_include_uri()", data.targets_include_uri()
                print "  targets_include_rich_text()", data.targets_include_rich_text()
                print "  tree_get_row_drag_data()", data.tree_get_row_drag_data()
                print "  get_pixbuf()", data.get_pixbuf()
            
            context.finish(False, False, time) #unsuccessful drop at time, don't delete source


    def on_sort_toggle(self, radioaction, current):
        print "on sort toggle"
        print "radioaction: ", radioaction
        print "current: ", current
        #TODO: Implement, add icon for mod-sort using 'stock_form-time-field'

    def on_add_folder(self, b):
        print "on_add_folder: ", b
        response = self.dialog_select_folder.run()
        if response == gtk.RESPONSE_OK:
            folder = self.dialog_select_folder.get_filename()
            print "Adding '%s' to monitors..." % folder #DEBUG
            self.add_folder(folder, False)
        elif response == gtk.RESPONSE_CANCEL:
            print "Cancelled.  Nothing selected." #DEBUG
        self.dialog_select_folder.hide()


    def on_monitor_activate(self, action, data=None):
        #print "ACTIVATED! ", a
        #print "The path: ", data
        pass


    def run(self, windowed=False):
        GnomePanelApplet.run(self, windowed)

    def _cleanup_before_destroy(self):

        print "safe cleanup....Please, wait..."
        self.label.set_text("stopping notifier...")
        self.queue_draw()
        #gtk.gdk.flush()

        self.notifier.stop()
        
        print "shutting down..."
        self.label.set_text("Shutting Down...")
        gtk.gdk.flush()


gobject.type_register(FileListApplet)


def applet_init(applet, iid):
    print "applet_init()"
    print applet, iid
    return applet.applet_factory(iid)

def run_applet(windowed=False):
    print "run_applet()"
    print "windowed = ",
    if windowed:
            print windowed
            applet = FileListApplet()
            main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
            main_window.set_title("File List Applet")
            main_window.set_default_size(200,24)
            main_window.connect("destroy", gtk.main_quit)
            applet.applet_factory(None)
            applet.reparent(main_window)
            main_window.show_all()
            gtk.gdk.threads_init() # Important
            gtk.main()
            sys.exit()

    else:
        print windowed
        gtk.gdk.threads_init() # Important
        gnomeapplet.bonobo_factory( FileListApplet.baiid, \
                                            FileListApplet.__gtype__, \
                                            FileListApplet.description, \
                                            FileListApplet.version, \
                                            applet_init)




if __name__ == "__main__":

    if len(sys.argv) > 2:
        # It's either the bonobo factory, or an error
        if "--oaf-activate-iid="+FileListApplet.baiid in sys.argv:
            run_applet(windowed=False) # ran from bonobo factory
        else:
            print "Too many arguments. Either 'window' as an argument or none at all."
            sys.exit()

    elif len(sys.argv) == 2:
        if "window" in sys.argv[1].lower():
            #app.run(windowed=True)
            run_applet(windowed=True) # ran explicitly in a window
        
        else:
            print "Invalid argument: ", sys.argv[1]
            sys.exit()

    else: #len(sys.argv) == 1:
        print "Ran directly from console..."
        run_applet(windowed=False) # ran directly from console


