//===========================================
//  Lumina-DE source code
//  Copyright (c) 2012-2015, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "LDesktop.h"
#include "LSession.h"
#include <QClipboard>
#include <QMimeData>

#include <LuminaOS.h>
#include <LuminaX11.h>
#include "LWinInfo.h"
#include "JsonMenu.h"

#include <QScreen>

#define DEBUG 1

LDesktop::LDesktop(int deskNum, bool setdefault) : QObject(){
  screenID = QApplication::screens().at(deskNum)->name();
  DPREFIX = "desktop-"+screenID+"/";
  i_dlg_folder = true;
  inputDLG = 0;
  defaultdesktop = setdefault; //(desktop->screenGeometry(desktopnumber).x()==0);
  //desktoplocked = true;
  issyncing = bgupdating = false;
  usewinmenu=false;
  desktopFolderActionMenu = 0;
  //Setup the internal variables
  settings = new QSettings(QSettings::UserScope, "lumina-desktop","desktopsettings", this);
  //qDebug() << " - Desktop Settings File:" << settings->fileName();
  if(!QFile::exists(settings->fileName())){ settings->setValue(DPREFIX+"background/filelist",QStringList()<<"default"); settings->sync(); }
  //bgWindow = 0;
  bgDesktop = 0;
  QTimer::singleShot(1,this, SLOT(InitDesktop()) );

}

LDesktop::~LDesktop(){
  delete deskMenu;
  delete winMenu;
  delete desktopFolderActionMenu;
  //delete bgWindow;
  delete workspacelabel;
  delete wkspaceact;
}

int LDesktop::Screen(){
  QList<QScreen*> scrns = QApplication::screens();
  for(int i=0; i<scrns.length(); i++){
    if(scrns[i]->name()==screenID){ return i; }
  }
  return -1;
}

void LDesktop::show(){
  //if(bgWindow!=0){ bgWindow->show(); }
  if(bgDesktop!=0){ bgDesktop->show(); }
  for(int i=0; i<PANELS.length(); i++){ PANELS[i]->show(); }
}

void LDesktop::hide(){
  //if(bgWindow!=0){ bgWindow->hide(); }
  if(bgDesktop!=0){ bgDesktop->hide(); }
  for(int i=0; i<PANELS.length(); i++){ PANELS[i]->hide(); }
}

void LDesktop::prepareToClose(){
  //Get any panels ready to close
  issyncing = true; //Stop handling any watcher events
  for(int i=0; i<PANELS.length(); i++){ PANELS[i]->prepareToClose(); PANELS.takeAt(i)->deleteLater(); i--; }
  //Now close down any desktop plugins
  //desktoplocked = true; //make sure that plugin settings are preserved during removal
  //Remove all the current containers
  bgDesktop->cleanup();
}

WId LDesktop::backgroundID(){
  if(bgDesktop!=0){ return bgDesktop->winId(); }
  else{ return QX11Info::appRootWindow(); }
}

QRect LDesktop::availableScreenGeom(){
  //Return a QRect containing the (global) screen area that is available (not under any panels)
  if(bgDesktop!=0){
    return globalWorkRect; //saved from previous calculations
  }else{
    return LSession::handle()->screenGeom( Screen() );
  }
}

void LDesktop::UpdateGeometry(){
    //First make sure there is something different about the geometry
    //if(desktop->screenGeometry(Screen())==bgWindow->geometry()){ return; }
    //Now update the screen
    // NOTE: This functionality is highly event-driven based on X changes - so we need to keep things in order (no signals/slots)
    //qDebug() << "Changing Desktop Geom:" << Screen();
    //bgWindow->setGeometry(desktop->screenGeometry(Screen()));
    /*for(int i=0; i<PANELS.length(); i++){
      PANELS[i]->UpdatePanel(true); //geom only updates - do this before adjusting the background
    }*/
    //qDebug() << " - Update Desktop Plugin Area";
    //UpdateDesktopPluginArea();
    //qDebug() << " - Done With Desktop Geom Updates";
    QTimer::singleShot(0, this, SLOT(UpdateBackground()));
    QTimer::singleShot(0, this, SLOT(UpdatePanels()));
}

void LDesktop::SystemLock(){
  QProcess::startDetached("xscreensaver-command -lock");
}

void LDesktop::SystemLogout(){
  LSession::handle()->systemWindow();
}

void LDesktop::SystemTerminal(){
  LSession::handle()->sessionSettings()->sync(); //make sure it is up to date
  QString term = LXDG::findDefaultAppForMime("application/terminal"); //LSession::handle()->sessionSettings()->value("default-terminal","xterm").toString();
  if(term.isEmpty() ||(!term.endsWith(".desktop") && !LUtils::isValidBinary(term)) ){ term = "xterm"; }
  LSession::LaunchApplication("lumina-open \""+term+"\"");
}

void LDesktop::SystemFileManager(){
  //Just open the home directory
  QString fm =  "lumina-open \""+QDir::homePath()+"\"";
  LSession::LaunchApplication(fm);
}

void LDesktop::SystemApplication(QAction* act){
  if(!act->whatsThis().isEmpty() && act->parent()==deskMenu){
    LSession::LaunchApplication("lumina-open \""+act->whatsThis()+"\"");
  }
}

void LDesktop::checkResolution(){
  //Compare the current screen resolution with the last one used/saved and adjust config values *only*
  //NOTE: This is only run the first time this desktop is created (before loading all the interface) - not on each desktop change
  int oldWidth = settings->value(DPREFIX+"screen/lastWidth",-1).toInt();
  int oldHeight = settings->value(DPREFIX+"screen/lastHeight",-1).toInt();
  QRect scrn = LSession::handle()->screenGeom( Screen() );
  if(scrn.isNull()){ return; }
  issyncing = true;
  settings->setValue(DPREFIX+"screen/lastWidth",scrn.width());
  settings->setValue(DPREFIX+"screen/lastHeight",scrn.height());

  if(oldWidth<1 || oldHeight<1 || scrn.width()<1 || scrn.height()<1){
    //nothing to do - something invalid
  }else if(scrn.width()==oldWidth && scrn.height()==oldHeight){
    //nothing to do - same as before
  }else{
    //Calculate the scale factor between the old/new sizes in each dimension
    //  and forward that on to all the interface elements
    double xscale = scrn.width()/((double) oldWidth);
    double yscale = scrn.height()/((double) oldHeight);
    if(DEBUG){
      qDebug() << "Screen Resolution Changed:" << screenID;
      qDebug() << " - Old:" << QString::number(oldWidth)+"x"+QString::number(oldHeight);
      qDebug() << " - New:" << QString::number(scrn.width())+"x"+QString::number(scrn.height());
      qDebug() << " - Scale Factors:" << xscale << yscale;
    }
    //Update any panels in the config file
    for(int i=0; i<4; i++){
      QString PPREFIX = "panel"+QString::number(Screen())+"."+QString::number(i)+"/";
      int ht = settings->value(PPREFIX+"height",-1).toInt();
      if(ht<1){ continue; } //no panel height defined
      QString loc = settings->value(PPREFIX+"location","top").toString().toLower();
      if(loc=="top" || loc=="bottom"){
        settings->setValue(PPREFIX+"height", (int) ht*yscale); //vertical dimension
      }else{
        settings->setValue(PPREFIX+"height", (int) ht*xscale); //horizontal dimension
      }
    }
    //Update any desktop plugins
    QStringList plugs = settings->value(DPREFIX+"pluginlist").toStringList();
    QFileInfoList files = LSession::handle()->DesktopFiles();
    for(int i=0; i<files.length(); i++){
      plugs << "applauncher::"+files[i].absoluteFilePath()+"---"+DPREFIX;
    }
    //QString pspath = QDir::homePath()+"/.lumina/desktop-plugins/%1.conf";
    QSettings *DP = LSession::handle()->DesktopPluginSettings();
    QStringList keys = DP->allKeys();
    for(int i=0; i<plugs.length(); i++){
      QStringList filter = keys.filter(plugs[i]);
      for(int j=0; j<filter.length(); j++){
        //Has existing settings - need to adjust it
	  if(filter[j].endsWith("location/height")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); }
	  if(filter[j].endsWith("location/width")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*xscale) ); }
	  if(filter[j].endsWith("location/x")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*xscale) ); }
	  if(filter[j].endsWith("location/y")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); }
	  if(filter[j].endsWith("IconSize")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); }
	  if(filter[j].endsWith("iconsize")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); }
      }
    }
    DP->sync(); //make sure it gets saved to disk right away

  }
  issyncing = false;
}

// =====================
//     PRIVATE SLOTS
// =====================
void LDesktop::InitDesktop(){
  //This is called *once* during the main initialization routines
  checkResolution(); //Adjust the desktop config file first (if necessary)
  if(DEBUG){ qDebug() << "Init Desktop:" << Screen(); }
    //connect(desktop, SIGNAL(resized(int)), this, SLOT(UpdateGeometry(int)));
  if(DEBUG){ qDebug() << "Desktop #"<<Screen()<<" -> "<< LSession::desktop()->screenGeometry(Screen()) << LSession::handle()->screenGeom(Screen()); }
  deskMenu = new QMenu(0);
    connect(deskMenu, SIGNAL(triggered(QAction*)), this, SLOT(SystemApplication(QAction*)) );
  winMenu = new QMenu(0);
    winMenu->setTitle(tr("Window List"));
    winMenu->setIcon( LXDG::findIcon("preferences-system-windows","") );
  connect(winMenu, SIGNAL(triggered(QAction*)), this, SLOT(winClicked(QAction*)) );
  workspacelabel = new QLabel(0);
    workspacelabel->setAlignment(Qt::AlignCenter);
  wkspaceact = new QWidgetAction(0);
    wkspaceact->setDefaultWidget(workspacelabel);
  bgtimer = new QTimer(this);
    bgtimer->setSingleShot(true);
    connect(bgtimer, SIGNAL(timeout()), this, SLOT(UpdateBackground()) );

    connect(QApplication::instance(), SIGNAL(DesktopConfigChanged()), this, SLOT(SettingsChanged()) );
    connect(QApplication::instance(), SIGNAL(DesktopFilesChanged()), this, SLOT(UpdateDesktop()) );
    connect(QApplication::instance(), SIGNAL(MediaFilesChanged()), this, SLOT(UpdateDesktop()) );
    connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(LocaleChanged()) );
    connect(QApplication::instance(), SIGNAL(WorkspaceChanged()), this, SLOT(UpdateBackground()) );
  //if(DEBUG){ qDebug() << "Create bgWindow"; }
  /*bgWindow = new QWidget(); //LDesktopBackground();
	bgWindow->setObjectName("bgWindow");
	bgWindow->setContextMenuPolicy(Qt::CustomContextMenu);
	bgWindow->setFocusPolicy(Qt::StrongFocus);
  	bgWindow->setWindowFlags(Qt::WindowStaysOnBottomHint | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
	LSession::handle()->XCB->SetAsDesktop(bgWindow->winId());
	bgWindow->setGeometry(LSession::handle()->screenGeom(Screen()));
        bgWindow->setWindowOpacity(0.0);
	connect(bgWindow, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ShowMenu()) );*/
  if(DEBUG){ qDebug() << "Create bgDesktop"; }
  bgDesktop = new LDesktopPluginSpace();
      int grid = settings->value(DPREFIX+"GridSize",-1).toInt();
      if(grid<0 && LSession::desktop()->screenGeometry(Screen()).height() > 2000){ grid = 200; }
      else if(grid<0){ grid = 100; }
      bgDesktop->SetIconSize( grid );
      bgDesktop->setContextMenuPolicy(Qt::CustomContextMenu);
      //LSession::handle()->XCB->SetAsDesktop(bgDesktop->winId());
      connect(bgDesktop, SIGNAL(PluginRemovedByUser(QString)), this, SLOT(RemoveDeskPlugin(QString)) );
      connect(bgDesktop, SIGNAL(IncreaseIcons()), this, SLOT(IncreaseDesktopPluginIcons()) );
      connect(bgDesktop, SIGNAL(DecreaseIcons()), this, SLOT(DecreaseDesktopPluginIcons()) );
      connect(bgDesktop, SIGNAL(HideDesktopMenu()), deskMenu, SLOT(hide()));
      connect(bgDesktop, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ShowMenu()) );

  desktopFolderActionMenu = new QMenu(0);
    desktopFolderActionMenu->setTitle(tr("Desktop Actions"));
    desktopFolderActionMenu->setIcon(LXDG::findIcon("user-desktop",""));
    desktopFolderActionMenu->addAction(LXDG::findIcon("folder-new",""), tr("New Folder"), this, SLOT(NewDesktopFolder()) );
    desktopFolderActionMenu->addAction(LXDG::findIcon("document-new",""), tr("New File"), this, SLOT(NewDesktopFile()) );
    desktopFolderActionMenu->addAction(LXDG::findIcon("edit-paste",""), tr("Paste"), this, SLOT(PasteInDesktop()) );
  if(DEBUG){ qDebug() << " - Desktop Init Done:" << screenID; }
  //Start the update processes
  QTimer::singleShot(10,this, SLOT(UpdateMenu()) );
  QTimer::singleShot(0,this, SLOT(UpdateBackground()) );
  QTimer::singleShot(1,this, SLOT(UpdateDesktop()) );
  QTimer::singleShot(2,this, SLOT(UpdatePanels()) );
}

void LDesktop::SettingsChanged(){
  if(issyncing){ return; } //don't refresh for internal modifications to the
  issyncing = true;
  qDebug() << "Found Settings Change:" << screenID;
  settings->sync(); //make sure to sync with external settings changes
  UpdateBackground();
  UpdateDesktop();
  UpdatePanels();
  UpdateMenu();
  issyncing = false;
  QTimer::singleShot(50, this, SLOT(UnlockSettings()) ); //give it a few moments to settle before performing another sync
}

void LDesktop::LocaleChanged(){
  //Update any elements which require a re-translation
  UpdateMenu(false); //do the full menu refresh
}

void LDesktop::UpdateMenu(bool fast){
  if(DEBUG){ qDebug() << " - Update Menu:" << screenID; }
  //Put a label at the top
  int num = LSession::handle()->XCB->CurrentWorkspace(); //LX11::GetCurrentDesktop();
  if(DEBUG){ qDebug() << "Found workspace number:" << num; }
  if(num < 0){ workspacelabel->setText( "<b>"+tr("Lumina Desktop")+"</b>"); }
  else{ workspacelabel->setText( "<b>"+QString(tr("Workspace %1")).arg(QString::number(num+1))+"</b>"); }
  if(fast && usewinmenu){ UpdateWinMenu(); }
  if(fast){ return; } //already done
  deskMenu->clear(); //clear it for refresh
  deskMenu->addAction(wkspaceact);
  deskMenu->addSeparator();
  //Now load the user's menu setup and fill the menu
  QStringList items = settings->value("menu/itemlist", QStringList()<< "terminal" << "filemanager" <<"applications" << "line" << "settings" ).toStringList();
  usewinmenu=false;
  for(int i=0; i<items.length(); i++){
    if(items[i]=="terminal"){ deskMenu->addAction(LXDG::findIcon("utilities-terminal",""), tr("Terminal"), this, SLOT(SystemTerminal()) ); }
    else if(items[i]=="lockdesktop"){ deskMenu->addAction(LXDG::findIcon("system-lock-screen",""), tr("Lock Session"), this, SLOT(SystemLock()) ); }
    else if(items[i]=="filemanager"){ deskMenu->addAction( LXDG::findIcon("user-home",""), tr("Browse Files"), this, SLOT(SystemFileManager()) ); }
    else if(items[i]=="applications"){ deskMenu->addMenu( LSession::handle()->applicationMenu() ); }
    else if(items[i]=="line"){ deskMenu->addSeparator(); }
    else if(items[i]=="settings"){ deskMenu->addMenu( LSession::handle()->settingsMenu() ); }
    else if(items[i]=="windowlist"){ deskMenu->addMenu( winMenu); usewinmenu=true;}
    else if(items[i].startsWith("app::::") && items[i].endsWith(".desktop")){
      //Custom *.desktop application
      QString file = items[i].section("::::",1,1).simplified();
      XDGDesktop xdgf(file);// = LXDG::loadDesktopFile(file, ok);
      if(xdgf.type!=XDGDesktop::BAD){
        deskMenu->addAction( LXDG::findIcon(xdgf.icon,""), xdgf.name)->setWhatsThis(file);
	}else{
	  qDebug() << "Could not load application file:" << file;
	}
    }else if(items[i].startsWith("jsonmenu::::")){
      //Custom JSON menu system (populated on demand via external scripts/tools
      QStringList info = items[i].split("::::"); //FORMAT:[ "jsonmenu",exec,name, icon(optional)]
      if(info.length()>=3){
        qDebug() << "Custom JSON Menu Loaded:" << info;
        JsonMenu *tmp = new JsonMenu(info[1], deskMenu);
        tmp->setTitle(info[2]);
        connect(tmp, SIGNAL(triggered(QAction*)), this, SLOT(SystemApplication(QAction*)) );
        if(info.length()>=4){ tmp->setIcon( LXDG::findIcon(info[3],"") ); }
        deskMenu->addMenu(tmp);
      }
    }
  }
  //Now add the desktop folder options (if desktop is icon-enabled)
  if(settings->value(DPREFIX+"generateDesktopIcons",false).toBool()){
    deskMenu->addSeparator();
    deskMenu->addMenu(desktopFolderActionMenu);
  }
  //Now add the system quit options
  deskMenu->addSeparator();
  deskMenu->addAction(LXDG::findIcon("system-log-out",""), tr("Leave"), this, SLOT(SystemLogout()) );
}

void LDesktop::UpdateWinMenu(){
  winMenu->clear();
  //Get the current list of windows
  QList<WId> wins = LSession::handle()->XCB->WindowList();
  //Now add them to the menu
  for(int i=0; i<wins.length(); i++){
    LWinInfo info(wins[i]);
    bool junk;
    QAction *act = winMenu->addAction( info.icon(junk), info.text() );
      act->setData( QString::number(wins[i]) );
  }
}

void LDesktop::winClicked(QAction* act){
  LSession::handle()->XCB->ActivateWindow( act->data().toString().toULong() );
}

void LDesktop::UpdateDesktop(){
  if(DEBUG){ qDebug() << " - Update Desktop Plugins for screen:" << screenID; }
  QStringList plugins = settings->value(DPREFIX+"pluginlist", QStringList()).toStringList();
  if(defaultdesktop && plugins.isEmpty()){
    //plugins << "sample" << "sample" << "sample";
  }
  bool changed=false; //in case the plugin list needs to be changed
  //First make sure all the plugin names are unique
  for(int i=0; i<plugins.length(); i++){
	if(!plugins[i].contains("---") ){
	  int num=1;
	  while( plugins.contains(plugins[i]+"---"+QString::number(Screen())+"."+QString::number(num)) ){
	    num++;
	  }
	  plugins[i] = plugins[i]+"---"+screenID+"."+QString::number(num);
	  //plugins[i] = plugins[i]+"---"+QString::number(Screen())+"."+QString::number(num);
	  changed=true;
	}
  }
  if(changed){
    //save the modified plugin list to file (so per-plugin settings are preserved)
    issyncing=true; //don't let the change cause a refresh
    settings->setValue(DPREFIX+"pluginlist", plugins);
    settings->sync();
    QTimer::singleShot(200, this, SLOT(UnlockSettings()) );
  }
  //If generating desktop file launchers, add those in
  QStringList filelist;
  if(settings->value(DPREFIX+"generateDesktopIcons",false).toBool()){
    QFileInfoList files = LSession::handle()->DesktopFiles();
    for(int i=0; i<files.length(); i++){
	filelist << files[i].absoluteFilePath();
    }
  }
  //Also show anything available in the /media directory, and /run/media/USERNAME directory
  if(settings->value(DPREFIX+"generateMediaIcons",true).toBool()){
    QDir media("/media");
    QStringList mediadirs = media.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
    for(int i=0; i<mediadirs.length(); i++){
      filelist << media.absoluteFilePath(mediadirs[i]);
    }
    QDir userMedia(QString("/run/media/%1").arg(QDir::homePath().split("/").takeLast()));
    QStringList userMediadirs = userMedia.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
    for(int i=0; i<userMediadirs.length(); i++){
      filelist << userMedia.absoluteFilePath(userMediadirs[i]);
    }
    //qDebug() << "Found media Dirs:" << mediadirs << userMediadirs;
  }
  UpdateDesktopPluginArea();
  bgDesktop->LoadItems(plugins, filelist);
}

void LDesktop::RemoveDeskPlugin(QString ID){
  //This is called after a plugin is manually removed by the user
  //	just need to ensure that the plugin is also removed from the settings file
  QStringList plugs = settings->value(DPREFIX+"pluginlist", QStringList()).toStringList();
  if(plugs.contains(ID)){
    plugs.removeAll(ID);
    issyncing=true; //don't let the change cause a refresh
    settings->setValue(DPREFIX+"pluginlist", plugs);
    settings->sync();
    QTimer::singleShot(200, this, SLOT(UnlockSettings()) );
  }
}

void LDesktop::IncreaseDesktopPluginIcons(){
  int cur = settings->value(DPREFIX+"GridSize",-1).toInt();
  if(cur<0 &&LSession::desktop()->screenGeometry(Screen()).height() > 2000){ cur = 200; }
  else if(cur<0){  cur = 100; }
  cur+=16;
  issyncing=true; //don't let the change cause a refresh
  settings->setValue(DPREFIX+"GridSize",cur);
    settings->sync();
    QTimer::singleShot(200, this, SLOT(UnlockSettings()) );
  bgDesktop->SetIconSize(cur);
}

void LDesktop::DecreaseDesktopPluginIcons(){
  int cur = settings->value(DPREFIX+"GridSize",-1).toInt();
  if(cur<0 &&LSession::desktop()->screenGeometry(Screen()).height() > 2000){ cur = 200; }
  else if(cur<0){ cur = 100; }
  if(cur<32){ return; } //cannot get smaller than 16x16
  cur-=16;
  issyncing=true; //don't let the change cause a refresh
  settings->setValue(DPREFIX+"GridSize",cur);
    settings->sync();
    QTimer::singleShot(200, this, SLOT(UnlockSettings()) );
  bgDesktop->SetIconSize(cur);
}

void LDesktop::UpdatePanels(){
  if(DEBUG){ qDebug() << " - Update Panels For Screen:" << Screen(); }
  int panels = settings->value(DPREFIX+"panels", -1).toInt();
  //if(panels==-1 && defaultdesktop){ panels=1; } //need at least 1 panel on the primary desktop
  //Remove all extra panels
  for(int i=0; i<PANELS.length(); i++){
    if(panels <= PANELS[i]->number()){
      if(DEBUG){ qDebug() << " -- Remove Panel:" << PANELS[i]->number(); }
      PANELS[i]->prepareToClose();
      PANELS.takeAt(i)->deleteLater();
      i--;
    }
  }
  for(int i=0; i<panels; i++){
    //Check for a panel with this number
    bool found = false;
    for(int p=0; p<PANELS.length() && !found; p++){
      if(PANELS[p]->number() == i){
        found = true;
	if(DEBUG){ qDebug() << " -- Update panel "<< i; }
        //panel already exists - just update it
        QTimer::singleShot(0, PANELS[p], SLOT(UpdatePanel()) );
      }
    }
    if(!found){
      if(DEBUG){ qDebug() << " -- Create panel "<< i; }
      //New panel
      LPanel *pan = new LPanel(settings, screenID, i, bgDesktop);
      PANELS << pan;
      pan->show();
    }
  }
  //Give it a 1/2 second before ensuring that the visible desktop area is correct
  QTimer::singleShot(1500, this, SLOT(UpdateDesktopPluginArea()) );
}

void LDesktop::UpdateDesktopPluginArea(){
  QRegion visReg(LSession::desktop()->screenGeometry(Screen()) ); //visible region (not hidden behind a panel)
  QRect rawRect = visReg.boundingRect(); //initial value (screen size)
  //qDebug() << "Update Desktop Plugin Area:" << bgWindow->geometry();
  for(int i=0; i<PANELS.length(); i++){
    QRegion shifted = visReg;
    QString loc = settings->value(PANELS[i]->prefix()+"location","top").toString().toLower();
    int vis = PANELS[i]->visibleWidth();
    if(loc=="top"){
      if(!shifted.contains(QRect(rawRect.x(), rawRect.y(), rawRect.width(), vis))){ continue; }
      shifted.translate(0, (rawRect.top()+vis)-shifted.boundingRect().top() );
    }else if(loc=="bottom"){
      if(!shifted.contains(QRect(rawRect.x(), rawRect.bottom()-vis, rawRect.width(), vis))){ continue; }
      shifted.translate(0, (rawRect.bottom()-vis)-shifted.boundingRect().bottom());
    }else if(loc=="left"){
      if( !shifted.contains(QRect(rawRect.x(), rawRect.y(), vis,rawRect.height())) ){ continue; }
      shifted.translate((rawRect.left()+vis)-shifted.boundingRect().left() ,0);
    }else{  //right
      if(!shifted.contains(QRect(rawRect.right()-vis, rawRect.y(), vis,rawRect.height())) ){ continue; }
      shifted.translate((rawRect.right()-vis)-shifted.boundingRect().right(),0);
    }
    visReg = visReg.intersected( shifted );
  }
  //Now make sure the desktop plugin area is only the visible area
  QRect rec = visReg.boundingRect();
//  QRect rec = LSession::desktop()->availableGeometry(Screen());
  //qDebug() << " - DPArea: Panel-Adjusted rectangle:" << rec;
  //qDebug() << " - DPArea: Screen Geometry:" << LSession::desktop()->screenGeometry(Screen());
  //qDebug() << " - DPArea: Current Geometry:" << bgDesktop->geometry();
  //LSession::handle()->XCB->SetScreenWorkArea((unsigned int) Screen(), rec);
  //Now remove the X offset to place it on the current screen (needs widget-coords, not global)
  globalWorkRect = rec; //save this for later
  rec.moveTopLeft( QPoint( rec.x()-LSession::desktop()->screenGeometry(Screen()).x() , rec.y()-LSession::desktop()->screenGeometry(Screen()).y() ) );
  //qDebug() << "DPlug Area:" << rec << bgDesktop->geometry() << LSession::handle()->desktop()->availableGeometry(bgDesktop);
  if(rec.size().isNull() ){ return; } //|| rec == bgDesktop->geometry()){return; } //nothing changed
  //bgDesktop->show(); //make sure Fluxbox is aware of it *before* we start moving it
  bgDesktop->setGeometry( LSession::desktop()->screenGeometry(Screen()));
  //bgDesktop->resize(LSession::desktop()->screenGeometry(Screen()).size());
  //bgDesktop->move(LSession::desktop()->screenGeometry(Screen()).topLeft());
  bgDesktop->setDesktopArea( rec );
  bgDesktop->UpdateGeom(); //just in case the plugin space itself needs to do anything
  QTimer::singleShot(10, this, SLOT(UpdateBackground()) );
  //Re-paint the panels (just in case a plugin was underneath it and the panel is transparent)
  //for(int i=0; i<PANELS.length(); i++){ PANELS[i]->update(); }
  //Make sure to re-disable any WM control flags and reset geometry again
  LSession::handle()->XCB->SetDisableWMActions(bgDesktop->winId());
  //bgDesktop->setGeometry( LSession::desktop()->screenGeometry(Screen()));
  //qDebug() << "Desktop Geom:" << bgDesktop->geometry();
  //qDebug() << "Screen Geom:" <<  LSession::desktop()->screenGeometry(Screen());
}

void LDesktop::UpdateBackground(){
  //Get the current Background
  if(bgupdating || bgDesktop==0){ return; } //prevent multiple calls to this at the same time
  bgupdating = true;
  if(DEBUG){ qDebug() << " - Update Desktop Background for screen:" << Screen(); }
  //Get the list of background(s) to show
  QStringList bgL = settings->value(DPREFIX+"background/filelist-workspace-"+QString::number( LSession::handle()->XCB->CurrentWorkspace()), QStringList()).toStringList();
  if(bgL.isEmpty()){ bgL = settings->value(DPREFIX+"background/filelist", QStringList()).toStringList(); }
  if(bgL.isEmpty()){ bgL << LOS::LuminaShare()+"../wallpapers/lumina-nature"; } //Use this entire directory by default if nothing specified
  //qDebug() << " - List:" << bgL << CBG;
    //Remove any invalid files
    for(int i=0; i<bgL.length(); i++){
      if(bgL[i]=="default" || bgL[i].startsWith("rgb(") ){ continue; } //built-in definitions - treat them as valid
      if(bgL[i].isEmpty()){ bgL.removeAt(i); i--; }
      if( !QFile::exists(bgL[i]) ){
        //Quick Detect/replace for new path for Lumina wallpapers (change in 1.3.4)
        if(bgL[i].contains("/wallpapers/Lumina-DE/")){
          bgL[i] = bgL[i].replace("/wallpapers/Lumina-DE/", "/wallpapers/lumina-desktop/"); i--; //modify the path and re-check it
        }else{
          bgL.removeAt(i); i--;
        }
      }
    }
    if(bgL.isEmpty()){ bgL << "default"; } //always fall back on the default
  //Determine if the background needs to be changed
  //qDebug() << "BG List:" << bgL << oldBGL << CBG << bgtimer->isActive();
  if(bgL==oldBGL && !CBG.isEmpty() && bgtimer->isActive()){
    //No background change scheduled - just update the widget
    bgDesktop->update();
    bgupdating=false;
    return;
  }
  oldBGL = bgL; //save this for later
  //Determine which background to use next
  QString bgFile;
  while(bgFile.isEmpty() || QFileInfo(bgFile).isDir()){
    QString prefix;
    if(!bgFile.isEmpty()){
      //Got a directory - update the list of files and re-randomize the selection
      QStringList imgs = LUtils::imageExtensions();
      for(int i=0; i<imgs.length(); i++){ imgs[i].prepend("*."); }
      QDir tdir(bgFile);
      prefix=bgFile+"/";
      bgL = tdir.entryList(imgs, QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
      //If directory no longer has any valid images - remove it from list and try again
      if(bgL.isEmpty()){
        oldBGL.removeAll(bgFile); //invalid directory - remove it from the list for the moment
        bgL = oldBGL; //reset the list back to the original list (not within a directory)
      }
    }
    //Verify that there are files in the list - otherwise use the default
    if(bgL.isEmpty()){ bgFile="default"; break; }
    int index = ( qrand() % bgL.length() );
    if(index== bgL.indexOf(CBG)){ //if the current wallpaper was selected by the randomization again
      //Go to the next in the list
      if(index < 0 || index >= bgL.length()-1){ index = 0; } //if invalid or last item in the list - go to first
      else{ index++; } //go to next
    }
    bgFile = prefix+bgL[index];
  }
  //Save this file as the current background
  CBG = bgFile;
  //qDebug() << " - Set Background to:" << CBG << index << bgL;
  if( (bgFile.toLower()=="default")){ bgFile = LOS::LuminaShare()+"desktop-background.jpg"; }
  //Now set this file as the current background
  QString format = settings->value(DPREFIX+"background/format","stretch").toString();
  //bgWindow->setBackground(bgFile, format);
  QPixmap backPix = LDesktopBackground::setBackground(bgFile, format, LSession::handle()->screenGeom(Screen()));
  bgDesktop->setBackground(backPix);
  //Now reset the timer for the next change (if appropriate)
  if(bgtimer->isActive()){ bgtimer->stop(); }
  if(bgL.length()>1 || oldBGL.length()>1){
    //get the length of the timer (in minutes)
    int min = settings->value(DPREFIX+"background/minutesToChange",5).toInt();
    //restart the internal timer
    if(min > 0){
      bgtimer->start(min*60000); //convert from minutes to milliseconds
    }
  }
  //Now update the panel backgrounds
  for(int i=0; i<PANELS.length(); i++){
    PANELS[i]->update();
    PANELS[i]->show();
  }
  bgupdating=false;
}

//Desktop Folder Interactions
void LDesktop::i_dlg_finished(int ret){
  if(inputDLG==0){ return; }
  QString name = inputDLG->textValue();
  inputDLG->deleteLater();
  inputDLG = 0;
  if(name.isEmpty() || ret!=QDialog::Accepted){ return; } //do nothing
  if(i_dlg_folder){ NewDesktopFolder(name); }
  else{ NewDesktopFile(name); }
}

void LDesktop::NewDesktopFolder(QString name){
  if(name.isEmpty()){
    i_dlg_folder = true; //creating a new folder
    if(inputDLG == 0){
      inputDLG = new QInputDialog(0, Qt::Dialog | Qt::WindowStaysOnTopHint);
      inputDLG->setInputMode(QInputDialog::TextInput);
      inputDLG->setTextValue("");
      inputDLG->setTextEchoMode(QLineEdit::Normal);
      inputDLG->setLabelText( tr("New Folder") );
      connect(inputDLG, SIGNAL(finished(int)), this, SLOT(i_dlg_finished(int)) );
    }
    inputDLG->showNormal();
  }else{
    QDir desktop(QDir::homePath());
    if(desktop.exists(tr("Desktop"))){ desktop.cd(tr("Desktop")); } //translated folder
    else{ desktop.cd("Desktop"); } //default/english folder
    if(!desktop.exists(name)){ desktop.mkpath(name); }
  }
}

void LDesktop::NewDesktopFile(QString name){
  if(name.isEmpty()){
    i_dlg_folder = false; //creating a new file
    if(inputDLG == 0){
      inputDLG = new QInputDialog(0, Qt::Dialog | Qt::WindowStaysOnTopHint);
      inputDLG->setInputMode(QInputDialog::TextInput);
      inputDLG->setTextValue("");
      inputDLG->setTextEchoMode(QLineEdit::Normal);
      inputDLG->setLabelText( tr("New File") );
      connect(inputDLG, SIGNAL(finished(int)), this, SLOT(i_dlg_finished(int)) );
    }
    inputDLG->showNormal();
  }else{
    QDir desktop(QDir::homePath());
    if(desktop.exists(tr("Desktop"))){ desktop.cd(tr("Desktop")); } //translated folder
    else{ desktop.cd("Desktop"); } //default/english folder
    if(!desktop.exists(name)){
      QFile file(desktop.absoluteFilePath(name));
      if(file.open(QIODevice::WriteOnly) ){ file.close(); }
    }
  }
}

void LDesktop::PasteInDesktop(){
  const QMimeData *mime = QApplication::clipboard()->mimeData();
  QStringList files;
  if(mime->hasFormat("x-special/lumina-copied-files")){
    files = QString(mime->data("x-special/lumina-copied-files")).split("\n");
  }else if(mime->hasUrls()){
    QList<QUrl> urls = mime->urls();
    for(int i=0; i<urls.length(); i++){
      files << QString("copy::::")+urls[i].toLocalFile();
    }
  }
  //Now go through and paste all the designated files
  QString desktop = LUtils::standardDirectory(LUtils::Desktop);
  for(int i=0; i<files.length(); i++){
    QString path = files[i].section("::::",1,-1);
    if(!QFile::exists(path)){ continue; } //does not exist any more - move on to next
    QString newpath = desktop+"/"+path.section("/",-1);
    if(QFile::exists(newpath)){
      //Need to change the filename a bit to make it unique
      int n = 2;
      newpath = desktop+"/"+QString::number(n)+"_"+path.section("/",-1);
      while(QFile::exists(newpath)){ n++; newpath = desktop+"/"+QString::number(n)+"_"+path.section("/",-1); }
    }
    bool isdir = QFileInfo(path).isDir();
    if(files[i].section("::::",0,0)=="cut"){
      QFile::rename(path, newpath);
    }else{ //copy
      if(isdir){ QFile::link(path, newpath); } //symlink for directories - never do a full copy
      else{ QFile::copy(path, newpath); }
    }
  }
}