/*
 *  This file is part of RawTherapee.
 *
 *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
 *  Copyright (c) 2011 Oliver Duis <www.oliverduis.de>
 *  Copyright (c) 2011 Michael Ezra <www.michaelezra.com>
 *
 *  RawTherapee 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.
 * 
 *  RawTherapee 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 RawTherapee.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <filebrowser.h>
#include <glibmm.h>
#include <options.h>
#include <multilangmgr.h>
#include <clipboard.h>
#include <profilestore.h>
#include <procparamchangers.h>
#include <dfmanager.h>
#include <ffmanager.h>

extern Options options;

FileBrowser::FileBrowser () 
    : tbl(NULL),numFiltered(0) {

    fbih = new FileBrowserIdleHelper;
    fbih->fbrowser = this;
    fbih->destroyed = false;
    fbih->pending = 0;

  //  profileStore.parseProfiles ();

    signal_style_changed().connect( sigc::mem_fun(*this, &FileBrowser::styleChanged) );
    
    int p = 0;
    pmenu = new Gtk::Menu ();
    pmenu->attach (*(open = new Gtk::MenuItem (M("FILEBROWSER_POPUPOPEN"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(develop = new Gtk::MenuItem (M("FILEBROWSER_POPUPPROCESS"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
    pmenu->attach (*(rank[0] = new Gtk::MenuItem (M("FILEBROWSER_POPUPUNRANK"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(rank[1] = new Gtk::MenuItem (M("FILEBROWSER_POPUPRANK1"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(rank[2] = new Gtk::MenuItem (M("FILEBROWSER_POPUPRANK2"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(rank[3] = new Gtk::MenuItem (M("FILEBROWSER_POPUPRANK3"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(rank[4] = new Gtk::MenuItem (M("FILEBROWSER_POPUPRANK4"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(rank[5] = new Gtk::MenuItem (M("FILEBROWSER_POPUPRANK5"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
    pmenu->attach (*(trash = new Gtk::MenuItem (M("FILEBROWSER_POPUPTRASH"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(untrash = new Gtk::MenuItem (M("FILEBROWSER_POPUPUNTRASH"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
    pmenu->attach (*(rename = new Gtk::MenuItem (M("FILEBROWSER_POPUPRENAME"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(remove = new Gtk::MenuItem (M("FILEBROWSER_POPUPREMOVE"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(removeInclProc = new Gtk::MenuItem (M("FILEBROWSER_POPUPREMOVEINCLPROC"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
    pmenu->attach (*(selall = new Gtk::MenuItem (M("FILEBROWSER_POPUPSELECTALL"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(copyTo = new Gtk::MenuItem (M("FILEBROWSER_POPUPCOPYTO"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(moveTo = new Gtk::MenuItem (M("FILEBROWSER_POPUPMOVETO"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
    pmenu->attach (*(menuDF = new Gtk::MenuItem (M("FILEBROWSER_DARKFRAME"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(menuFF = new Gtk::MenuItem (M("FILEBROWSER_FLATFIELD"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
    pmenu->attach (*(copyprof = new Gtk::MenuItem (M("FILEBROWSER_COPYPROFILE"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(pasteprof = new Gtk::MenuItem (M("FILEBROWSER_PASTEPROFILE"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(partpasteprof = new Gtk::MenuItem (M("FILEBROWSER_PARTIALPASTEPROFILE"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(applyprof = new Gtk::MenuItem (M("FILEBROWSER_APPLYPROFILE"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(clearprof = new Gtk::MenuItem (M("FILEBROWSER_CLEARPROFILE"))), 0, 1, p, p+1); p++;
    pmenu->attach (*(cachemenu = new Gtk::MenuItem (M("FILEBROWSER_CACHE"))), 0, 1, p, p+1); p++;

    pmenu->show_all ();

    pmaccelgroup = Gtk::AccelGroup::create ();
    pmenu->set_accel_group (pmaccelgroup);
    selall->add_accelerator ("activate", pmenu->get_accel_group(), GDK_a, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
    trash->add_accelerator ("activate", pmenu->get_accel_group(), GDK_Delete, (Gdk::ModifierType)0, Gtk::ACCEL_VISIBLE);
    untrash->add_accelerator ("activate", pmenu->get_accel_group(), GDK_Delete, Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
    develop->add_accelerator ("activate", pmenu->get_accel_group(), GDK_Q, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
    copyprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_C, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
    pasteprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_V, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
    partpasteprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_V, Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);

    profmenu = new Gtk::Menu ();

    open->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), open));    
    for (int i=0; i<6; i++)
        rank[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), rank[i]));    
    trash->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), trash));    
    untrash->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), untrash));    
    develop->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), develop));    
    rename->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), rename));    
    remove->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), remove));    
    removeInclProc->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), removeInclProc));
    selall->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), selall));
    copyTo->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), copyTo));
    moveTo->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), moveTo));
    copyprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), copyprof));    
    pasteprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), pasteprof));    
    partpasteprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), partpasteprof));    
    applyprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), applyprof));    
    clearprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), clearprof));
    cachemenu->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), cachemenu));
}

void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) {

    trash->set_sensitive (false);
    untrash->set_sensitive (false);
    for (int i=0; i<selected.size(); i++) 
        if (((FileBrowserEntry*)selected[i])->thumbnail->getStage()==1) {
            untrash->set_sensitive (true);
            break;
        }
    for (int i=0; i<selected.size(); i++) 
        if (((FileBrowserEntry*)selected[i])->thumbnail->getStage()==0) {
            trash->set_sensitive (true);
            break;
        }

    pasteprof->set_sensitive (clipboard.hasProcParams());
    partpasteprof->set_sensitive (clipboard.hasProcParams());
    copyprof->set_sensitive (selected.size()==1);
    clearprof->set_sensitive (selected.size()>0);

    // submenu applmenu
    int p = 0;
    Gtk::Menu* applmenu = Gtk::manage (new Gtk::Menu ());
    std::vector<Glib::ustring> profnames = profileStore.getProfileNames ();
    for (int i=0; i<profnames.size(); i++) {
        Gtk::MenuItem* mi = Gtk::manage (new Gtk::MenuItem (profnames[i]));
        applmenu->attach (*mi, 0, 1, p, p+1); p++;
        mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyMenuItemActivated), profnames[i]));    
        mi->show ();
    }
    applyprof->set_submenu (*applmenu);    

    // submenuDF
    Gtk::Menu* submenuDF = Gtk::manage (new Gtk::Menu ());
    submenuDF->attach (*(selectDF = new Gtk::MenuItem (M("FILEBROWSER_SELECTDARKFRAME"))), 0, 1, p, p+1); p++;
    submenuDF->attach (*(autoDF = new Gtk::MenuItem (M("FILEBROWSER_AUTODARKFRAME"))), 0, 1, p, p+1); p++;
    submenuDF->attach (*(thisIsDF = new Gtk::MenuItem (M("FILEBROWSER_MOVETODARKFDIR"))), 0, 1, p, p+1); p++;
    selectDF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), selectDF));
	autoDF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), autoDF));
	thisIsDF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated),thisIsDF ));
	submenuDF->show_all ();
	menuDF->set_submenu (*submenuDF);

	// submenuFF
	Gtk::Menu* submenuFF = Gtk::manage (new Gtk::Menu ());
	submenuFF->attach (*(selectFF = new Gtk::MenuItem (M("FILEBROWSER_SELECTFLATFIELD"))), 0, 1, p, p+1); p++;
	submenuFF->attach (*(autoFF = new Gtk::MenuItem (M("FILEBROWSER_AUTOFLATFIELD"))), 0, 1, p, p+1); p++;
	submenuFF->attach (*(thisIsFF = new Gtk::MenuItem (M("FILEBROWSER_MOVETOFLATFIELDDIR"))), 0, 1, p, p+1); p++;
	selectFF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), selectFF));
	autoFF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), autoFF));
	thisIsFF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated),thisIsFF ));
	submenuFF->show_all ();
	menuFF->set_submenu (*submenuFF);

    // build cache sub menu
    p = 0;
	Gtk::Menu* cachesubmenu = Gtk::manage (new Gtk::Menu ());
	cachesubmenu->attach (*(clearFromCache = new Gtk::MenuItem (M("FILEBROWSER_CACHECLEARFROMPARTIAL"))), 0, 1, p, p+1); p++;
	cachesubmenu->attach (*(clearFromCacheFull = new Gtk::MenuItem (M("FILEBROWSER_CACHECLEARFROMFULL"))), 0, 1, p, p+1); p++;
    clearFromCache->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), clearFromCache));
    clearFromCacheFull->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), clearFromCacheFull));
    cachesubmenu->show_all ();
    cachemenu->set_submenu (*cachesubmenu);

    pmenu->popup (3, this->eventTime);
}

void FileBrowser::doubleClicked (ThumbBrowserEntryBase* entry) {

    if (tbl && entry) {
        std::vector<Thumbnail*> entries;
        entries.push_back (((FileBrowserEntry*)entry)->thumbnail);
        tbl->openRequested (entries);
    }
}

struct addparams {
    FileBrowserIdleHelper* fbih;
    FileBrowserEntry* entry;
};

int AddEntryUIThread (void* data) {
    
    addparams* ap = (addparams*) data;
    FileBrowserIdleHelper* fbih = ap->fbih;

    gdk_threads_enter();

    if (fbih->destroyed) {
        if (fbih->pending == 1)
            delete fbih;
        else    
            fbih->pending--;
        delete ap->entry;
        delete ap;
        gdk_threads_leave ();
        return 0;
    }

    ap->fbih->fbrowser->addEntry_ (ap->entry);
    delete ap;
    fbih->pending--;
    gdk_threads_leave();
    return 0;
}

void FileBrowser::addEntry (FileBrowserEntry* entry) {

    fbih->pending++;
    entry->setParent (this);
    addparams* ap = new addparams;
    ap->fbih = fbih;
    ap->entry = entry;
    g_idle_add (AddEntryUIThread, ap);
}

void FileBrowser::addEntry_ (FileBrowserEntry* entry) {

    entry->selected = false;
    entry->drawable = false;
    entry->framed = editedFiles.find (entry->filename)!=editedFiles.end();
        
    // add button set to the thumbbrowserentry
    entry->addButtonSet (new FileThumbnailButtonSet (entry));
    entry->getThumbButtonSet()->setRank (entry->thumbnail->getRank());
    entry->getThumbButtonSet()->setInTrash (entry->thumbnail->getStage()==1);
    entry->getThumbButtonSet()->setButtonListener (this);
    entry->resize (getCurrentThumbSize());

    // find place in abc order
	{
		// TODO: Check for Linux
		#ifdef WIN32
		Glib::Mutex::Lock lock(entryMutex);
		#endif

    std::vector<ThumbBrowserEntryBase*>::iterator i = fd.begin();
    while (i!=fd.end() && *entry < *((FileBrowserEntry*)*i))
        i++;
        
    fd.insert (i, entry);

    initEntry (entry);
	}
    redraw ();
}

FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname) {
	// TODO: Check for Linux
	#ifdef WIN32
	Glib::Mutex::Lock lock(entryMutex);
	#endif

    for (std::vector<ThumbBrowserEntryBase*>::iterator i=fd.begin(); i!=fd.end(); i++) 
        if ((*i)->filename==fname) {
            ThumbBrowserEntryBase* entry = *i;
            entry->selected = false;
            fd.erase (i);
            std::vector<ThumbBrowserEntryBase*>::iterator j = std::find (selected.begin(), selected.end(), entry);
            if (j!=selected.end()) {
                selected.erase (j);
                notifySelectionListener ();
            }
            if (lastClicked==entry)
                lastClicked = NULL;
            redraw ();
            return (FileBrowserEntry*)entry;
        }
    return NULL;
}

FileBrowserEntry* FileBrowser::findEntry (const Glib::ustring& fname) {
	// TODO: Check for Linux
	#ifdef WIN32
	Glib::Mutex::Lock lock(entryMutex);
	#endif

    for (std::vector<ThumbBrowserEntryBase*>::iterator i=fd.begin(); i!=fd.end(); i++) 
        if ((*i)->filename==fname) 
            return (FileBrowserEntry*)*i;
    return NULL;
}

void FileBrowser::close () {
    if (fbih->pending)
        fbih->destroyed = true;
    else
        delete fbih;

    fbih = new FileBrowserIdleHelper;
    fbih->fbrowser = this;
    fbih->destroyed = false;
    fbih->pending = 0;

	{
		// TODO: Check for Linux
		#ifdef WIN32
		Glib::Mutex::Lock lock(entryMutex);
		#endif

        
		selected.clear ();
		notifySelectionListener ();

        // The listener merges parameters with old values, so delete afterwards
    for (int i=0; i<fd.size(); i++)
    {
        delete fd[i];
    }
    fd.clear ();
	}

    lastClicked = NULL;
}

void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {

    std::vector<FileBrowserEntry*> mselected;
    for (int i=0; i<selected.size(); i++)
        mselected.push_back ((FileBrowserEntry*)selected[i]);

    if (!tbl || (m!=selall && mselected.size()==0) )
        return;

    for (int i=0; i<6; i++) 
        if (m==rank[i]) {
            rankingRequested (mselected, i);
            return;
        }       
    if (m==open) {
        std::vector<Thumbnail*> entries;
        for (int i=0; i<mselected.size(); i++)
            entries.push_back (mselected[i]->thumbnail);
        tbl->openRequested (entries);
     }
    else if (m==remove)
        tbl->deleteRequested (mselected, false);
	else if (m==removeInclProc)
        tbl->deleteRequested (mselected, true);
    else if (m==trash) 
        toTrashRequested (mselected);
    else if (m==untrash) 
        fromTrashRequested (mselected);
    else if (m==develop)
        tbl->developRequested (mselected);
    else if (m==rename)
        tbl->renameRequested (mselected);
    else if (m==selall) {
        lastClicked = NULL;
		{
			// TODO: Check for Linux
			#ifdef WIN32
			Glib::Mutex::Lock lock(entryMutex);
			#endif

        selected.clear ();
        for (int i=0; i<fd.size(); i++)
            if (checkFilter (fd[i])) {
                fd[i]->selected = true;
                selected.push_back (fd[i]);
            }
		}
        queue_draw ();
        notifySelectionListener ();
    }
    else if( m==copyTo){
    	tbl->copyMoveRequested (mselected, false);
    }

    else if( m==moveTo){
       	tbl->copyMoveRequested (mselected, true);
    }

    else if (m==autoDF){
		for (int i=0; i<mselected.size(); i++){
			rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
			pp.raw.df_autoselect= true;
			pp.raw.dark_frame.clear();
			mselected[i]->thumbnail->setProcParams(pp,FILEBROWSER,false);
		}
    }else if (m==selectDF){
    	if( mselected.size() > 0 ){
    		rtengine::procparams::ProcParams pp=mselected[0]->thumbnail->getProcParams();
    		Gtk::FileChooserDialog fc("Dark Frame",Gtk::FILE_CHOOSER_ACTION_OPEN );
    		fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
    		fc.add_button( Gtk::StockID("gtk-apply"), Gtk::RESPONSE_APPLY);
    		if( pp.raw.dark_frame.empty())
    		   fc.set_current_folder( options.rtSettings.darkFramesPath );
    		else
    		   fc.set_filename( pp.raw.dark_frame );
    		if( fc.run() == Gtk::RESPONSE_APPLY ){
    			for (int i=0; i<mselected.size(); i++){
    				rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
					pp.raw.dark_frame= fc.get_filename();
					pp.raw.df_autoselect= false;
					mselected[i]->thumbnail->setProcParams(pp,FILEBROWSER,false);
				}
			}
    	}
    }else if( m==thisIsDF){
    	if( options.rtSettings.darkFramesPath.size() >0 && Gio::File::create_for_path(options.rtSettings.darkFramesPath)->query_exists() ){
			for (int i=0; i<mselected.size(); i++){
				Glib::RefPtr<Gio::File> file = Gio::File::create_for_path ( mselected[i]->filename );
				if( !file )continue;
				Glib::ustring destName = options.rtSettings.darkFramesPath+ "/" + file->get_basename();
				Glib::RefPtr<Gio::File> dest = Gio::File::create_for_path ( destName );
				file->move(  dest );
			}
			// Reinit cache
			rtengine::dfm.init( options.rtSettings.darkFramesPath );
    	}
    }
    else if (m==autoFF){
		for (int i=0; i<mselected.size(); i++){
			rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
			pp.raw.ff_AutoSelect= true;
			pp.raw.ff_file.clear();
			mselected[i]->thumbnail->setProcParams(pp,FILEBROWSER,false);
		}
    }
    else if (m==selectFF){
    	if( mselected.size() > 0 ){
    		rtengine::procparams::ProcParams pp=mselected[0]->thumbnail->getProcParams();
    		Gtk::FileChooserDialog fc("Flat Field",Gtk::FILE_CHOOSER_ACTION_OPEN );
    		fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
    		fc.add_button( Gtk::StockID("gtk-apply"), Gtk::RESPONSE_APPLY);
    		if( pp.raw.ff_file.empty())
    		   fc.set_current_folder( options.rtSettings.flatFieldsPath );
    		else
    		   fc.set_filename( pp.raw.ff_file );
    		if( fc.run() == Gtk::RESPONSE_APPLY ){
    			for (int i=0; i<mselected.size(); i++){
    				rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
					pp.raw.ff_file= fc.get_filename();
					pp.raw.ff_AutoSelect= false;
					mselected[i]->thumbnail->setProcParams(pp,FILEBROWSER,false);
				  }
			  }
    	}
    }
    else if( m==thisIsFF){
    	if( options.rtSettings.flatFieldsPath.size() >0 && Gio::File::create_for_path(options.rtSettings.flatFieldsPath)->query_exists() ){
			for (int i=0; i<mselected.size(); i++){
				Glib::RefPtr<Gio::File> file = Gio::File::create_for_path ( mselected[i]->filename );
				if( !file )continue;
				Glib::ustring destName = options.rtSettings.flatFieldsPath+ "/" + file->get_basename();
				Glib::RefPtr<Gio::File> dest = Gio::File::create_for_path ( destName );
				file->move(  dest );
			}
			// Reinit cache
			rtengine::ffm.init( options.rtSettings.flatFieldsPath );
    	}
    }
    else if (m==copyprof)
        copyProfile ();
    else if (m==pasteprof) 
        pasteProfile ();
    else if (m==partpasteprof) 
        partPasteProfile ();
    else if (m==clearprof) {
        for (int i=0; i<mselected.size(); i++) 
            mselected[i]->thumbnail->clearProcParams (FILEBROWSER);
        queue_draw ();
    }
	else if (m==clearFromCache) {
		for (int i=0; i<mselected.size(); i++)
			tbl->clearFromCacheRequested (mselected, false);
		//queue_draw ();
    }
	else if (m==clearFromCacheFull) {
		for (int i=0; i<mselected.size(); i++)
			tbl->clearFromCacheRequested (mselected, true);
		//queue_draw ();
    }
}

void FileBrowser::copyProfile () {

    if (selected.size()==1)
        clipboard.setProcParams (((FileBrowserEntry*)selected[0])->thumbnail->getProcParams());
}

void FileBrowser::pasteProfile () {

    std::vector<FileBrowserEntry*> mselected;
    for (int i=0; i<selected.size(); i++)
        mselected.push_back ((FileBrowserEntry*)selected[i]);

    if (!tbl || mselected.size()==0)
        return;

    for (int i=0; i<mselected.size(); i++) 
        mselected[i]->thumbnail->setProcParams (clipboard.getProcParams(), FILEBROWSER);
    
    queue_draw ();
}

void FileBrowser::partPasteProfile () {

    std::vector<FileBrowserEntry*> mselected;
    for (int i=0; i<selected.size(); i++)
        mselected.push_back ((FileBrowserEntry*)selected[i]);

    if (!tbl || mselected.size()==0)
        return;

    if (partialPasteDlg.run ()) {
    
        for (int i=0; i<mselected.size(); i++) {
            rtengine::procparams::ProcParams params = mselected[i]->thumbnail->getProcParams ();
            partialPasteDlg.applyPaste (&params, &clipboard.getProcParams());
            mselected[i]->thumbnail->setProcParams (params, FILEBROWSER);
        }

        queue_draw ();
    }
    partialPasteDlg.hide ();
}

void FileBrowser::openDefaultViewer (int destination) {
    if (selected.size()==1)
        ((FileBrowserEntry*)selected[0])->thumbnail->openDefaultViewer(destination);
}

bool FileBrowser::keyPressed (GdkEventKey* event) {

    if ((event->keyval==GDK_C || event->keyval==GDK_c) && event->state & GDK_CONTROL_MASK) {
        copyProfile ();
        return true;
    }
    else if ((event->keyval==GDK_V || event->keyval==GDK_v) && event->state & GDK_CONTROL_MASK && !(event->state & GDK_SHIFT_MASK)) {
        pasteProfile ();
        return true;
    }
    else if ((event->keyval==GDK_V || event->keyval==GDK_v) && event->state & GDK_CONTROL_MASK && event->state & GDK_SHIFT_MASK) {
        partPasteProfile ();
        return true;
    }
    else if (event->keyval==GDK_Delete && !(event->state & GDK_SHIFT_MASK)) {
        menuItemActivated (trash);
        return true;
    }
    else if (event->keyval==GDK_Delete && event->state & GDK_SHIFT_MASK) {
        menuItemActivated (untrash);
        return true;
    }
    else if ((event->keyval==GDK_Q || event->keyval==GDK_q) && event->state & GDK_CONTROL_MASK) {
        menuItemActivated (develop);
        return true;
    }
    else if ((event->keyval==GDK_A || event->keyval==GDK_a) && event->state & GDK_CONTROL_MASK) {
        menuItemActivated (selall);
        return true;
    }
    else if (event->keyval==GDK_F5) {
        int dest = 1;
        if (event->state & GDK_SHIFT_MASK)
            dest = 2;
        else if (event->state & GDK_CONTROL_MASK)
            dest = 3;

        openDefaultViewer (dest);
        return true;
    }
    else if (event->keyval==GDK_Page_Up) {
        scrollPage(GDK_SCROLL_UP);
        return true;
    }
    else if (event->keyval==GDK_Page_Down) {
        scrollPage(GDK_SCROLL_DOWN);
        return true;
    }
        
    return false;
}

void FileBrowser::applyMenuItemActivated (Glib::ustring ppname) {

    rtengine::procparams::ProcParams* pparams = profileStore.getProfile (ppname);
    if (pparams && selected.size()>0) {
        for (int i=0; i<selected.size(); i++) 
            ((FileBrowserEntry*)selected[i])->thumbnail->setProcParams (*pparams, FILEBROWSER);
        queue_draw ();
    }
}

void FileBrowser::applyFilter (const BrowserFilter& filter) {

    this->filter = filter;

    // remove items not complying the filter from the selection
    bool selchanged = false;
    numFiltered=0;
	{
		// TODO: Check for Linux
		#ifdef WIN32
		Glib::Mutex::Lock lock(entryMutex);
		#endif

    for (int i=0; i<fd.size(); i++)
    	if(checkFilter (fd[i]))
    		numFiltered++;
    	else if (fd[i]->selected ) {
            fd[i]->selected = false;
            std::vector<ThumbBrowserEntryBase*>::iterator j = std::find (selected.begin(), selected.end(), fd[i]);
            selected.erase (j);
            if (lastClicked==fd[i])
                lastClicked = NULL;
            selchanged = true;
        }
	}

    if (selchanged)
        notifySelectionListener ();
    redraw ();
}

bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) { // true -> entry complies filter
    
    FileBrowserEntry* entry = (FileBrowserEntry*)entryb;
    // return false if basic filter settings are not satisfied
    if (filter.showRanked[entry->thumbnail->getRank()]==false || (entry->thumbnail->getStage()==1 && !filter.showTrash) || (entry->thumbnail->getStage()==0 && !filter.showNotTrash))
        return false;
    
    // check exif filter
    const CacheImageData* cfs = entry->thumbnail->getCacheImageData();
    double tol = 0.01;
    double tol2 = 1e-8;
    
	if (!filter.exifFilterEnabled)
		return true;
	
	if (!cfs->exifValid)
		return (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(cfs->camera)>0) 
			&& (!filter.exifFilter.filterLens || filter.exifFilter.lenses.count(cfs->lens)>0)
			&& (!filter.exifFilter.filterFiletype || filter.exifFilter.filetypes.count(cfs->filetype)>0);
		
    return 
         (!filter.exifFilter.filterShutter || (rtengine::ImageMetaData::shutterFromString(rtengine::ImageMetaData::shutterToString(cfs->shutter)) >= filter.exifFilter.shutterFrom-tol2 && rtengine::ImageMetaData::shutterFromString(rtengine::ImageMetaData::shutterToString(cfs->shutter)) <= filter.exifFilter.shutterTo+tol2))
      && (!filter.exifFilter.filterFNumber || (rtengine::ImageMetaData::apertureFromString(rtengine::ImageMetaData::apertureToString(cfs->fnumber)) >= filter.exifFilter.fnumberFrom-tol2 && rtengine::ImageMetaData::apertureFromString(rtengine::ImageMetaData::apertureToString(cfs->fnumber)) <= filter.exifFilter.fnumberTo+tol2))
      && (!filter.exifFilter.filterFocalLen || (cfs->focalLen >= filter.exifFilter.focalFrom-tol && cfs->focalLen <= filter.exifFilter.focalTo+tol))
	  && (!filter.exifFilter.filterISO     || (cfs->iso >= filter.exifFilter.isoFrom && cfs->iso <= filter.exifFilter.isoTo))
      && (!filter.exifFilter.filterCamera  || filter.exifFilter.cameras.count(cfs->camera)>0)
	  && (!filter.exifFilter.filterLens    || filter.exifFilter.lenses.count(cfs->lens)>0)
	    && (!filter.exifFilter.filterFiletype  || filter.exifFilter.filetypes.count(cfs->filetype)>0);
}

void FileBrowser::toTrashRequested (std::vector<FileBrowserEntry*> tbe) {

    for (int i=0; i<tbe.size(); i++) {
        if (tbe[i]->thumbnail->getStage()==1)
            continue;               
        tbe[i]->thumbnail->setStage (1);
        if (tbe[i]->getThumbButtonSet()) {
            tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank());
            tbe[i]->getThumbButtonSet()->setInTrash (true);
            tbe[i]->thumbnail->updateCache(); // needed to save the rank to disk
        }
    }
    trash_changed().emit();
    applyFilter (filter);
}

void FileBrowser::fromTrashRequested (std::vector<FileBrowserEntry*> tbe) {

    for (int i=0; i<tbe.size(); i++) {
        if (tbe[i]->thumbnail->getStage()==0)
            continue;
        tbe[i]->thumbnail->setStage (0);
        if (tbe[i]->getThumbButtonSet()) {
            tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank());
            tbe[i]->getThumbButtonSet()->setInTrash (false);
            tbe[i]->thumbnail->updateCache(); // needed to save the rank to disk
        }
    }
    trash_changed().emit();
    applyFilter (filter);
}

void FileBrowser::rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank) {

    for (int i=0; i<tbe.size(); i++) {
        tbe[i]->thumbnail->setRank (rank);
        tbe[i]->thumbnail->updateCache(); // needed to save the rank to disk
        if (tbe[i]->getThumbButtonSet())
                tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank());
    }
    applyFilter (filter);
}

void FileBrowser::buttonPressed (LWButton* button, int actionCode, void* actionData) {

    if (actionCode>=0 && actionCode<=5) { // rank
        std::vector<FileBrowserEntry*> tbe;
        tbe.push_back ((FileBrowserEntry*)actionData);
        rankingRequested (tbe, actionCode);
    }
    else if (actionCode==6 && tbl) { // to processin queue
        std::vector<FileBrowserEntry*> tbe;
        tbe.push_back ((FileBrowserEntry*)actionData);
        tbl->developRequested (tbe);
    }
    else if (actionCode==7) { // to trash / undelete
        std::vector<FileBrowserEntry*> tbe;
        FileBrowserEntry* entry = (FileBrowserEntry*)actionData;
        tbe.push_back (entry);
        if (entry->thumbnail->getStage()==0)
            toTrashRequested (tbe);
        else
            fromTrashRequested (tbe);
    }
}

void FileBrowser::openNextImage () {
	// TODO: Check for Linux
	#ifdef WIN32
	Glib::Mutex::Lock lock(entryMutex);
	#endif

    if (fd.size()>0) {
        for (int i=fd.size()-1; i>=0; i--)
            if (editedFiles.find (fd[i]->filename)!=editedFiles.end()) 
                if (i<fd.size()-1 && tbl) {
                    std::vector<Thumbnail*> entries;
                    entries.push_back (((FileBrowserEntry*)fd[i+1])->thumbnail);
                    tbl->openRequested (entries);
                    return;
                }
        if (tbl) {
            std::vector<Thumbnail*> entries;
            entries.push_back (((FileBrowserEntry*)fd[0])->thumbnail);
            tbl->openRequested (entries);
        }
    }
}

void FileBrowser::openPrevImage () {
	// TODO: Check for Linux
	#ifdef WIN32
	Glib::Mutex::Lock lock(entryMutex);
	#endif

    if (fd.size()>0) {
        for (int i=0; i<fd.size(); i++)
            if (editedFiles.find (fd[i]->filename)!=editedFiles.end()) 
                if (i>0 && tbl) {
                    std::vector<Thumbnail*> entries;
                    entries.push_back (((FileBrowserEntry*)fd[i-1])->thumbnail);
                    tbl->openRequested (entries);
                    return;
                }
        if (tbl) {
            std::vector<Thumbnail*> entries;
            entries.push_back (((FileBrowserEntry*)fd[fd.size()-1])->thumbnail);
            tbl->openRequested (entries);
        }
    }
}

int redrawtb (void* data) {

    ((FileBrowser*)data)->_thumbRearrangementNeeded ();
    return 0;
}

void FileBrowser::_thumbRearrangementNeeded () {

    refreshThumbImages ();
}

void FileBrowser::thumbRearrangementNeeded () {

    g_idle_add (redrawtb, this);
}
void FileBrowser::selectionChanged () {

    notifySelectionListener ();
}

void FileBrowser::notifySelectionListener () {

    if (tbl) {
        std::vector<Thumbnail*> thm;
        for (int i=0; i<selected.size(); i++)
            thm.push_back (((FileBrowserEntry*)selected[i])->thumbnail);
        tbl->selectionChanged (thm);
    }    
}

void FileBrowser::redrawNeeded (ThumbBrowserEntryBase* entry) {

    if (entry->insideWindow (0, 0, internal.get_width(), internal.get_height())) {
        if (!internal.isDirty ()) {
            internal.setDirty ();
            internal.queue_draw ();
        }
    }        
}

void FileBrowser::redrawNeeded (LWButton* button) {
    
    queue_draw ();
}
FileBrowser::type_trash_changed FileBrowser::trash_changed () {
    return m_trash_changed;
}
