1
0
mirror of git://projects.qi-hardware.com/gmenu2x.git synced 2025-01-01 06:03:55 +02:00
gmenu2x/src/menu.cpp
Maarten ter Huurne dae5627091 Removed PXML support.
PXML is the metadata format for Pandora packages. It is not used on the
NanoNote or under Dingux.
Ideally it would be a compilation option, but without an active upstream it
would not receive any testing and therefore likely be broken. So in my
opinion it is more time efficient to remove it now and re-add if it is ever
wanted again.
2010-06-17 19:37:21 +02:00

438 lines
12 KiB
C++

/***************************************************************************
* Copyright (C) 2006 by Massimiliano Torromeo *
* massimiliano.torromeo@gmail.com *
* *
* 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 2 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <algorithm>
#include <math.h>
#include "gmenu2x.h"
#include "linkapp.h"
#include "menu.h"
#include "filelister.h"
#include "utilities.h"
using namespace std;
Menu::Menu(GMenu2X *gmenu2x) {
this->gmenu2x = gmenu2x;
iFirstDispSection = 0;
DIR *dirp;
struct stat st;
struct dirent *dptr;
string filepath;
if ((dirp = opendir("sections")) == NULL) return;
while ((dptr = readdir(dirp))) {
if (dptr->d_name[0]=='.') continue;
filepath = (string)"sections/"+dptr->d_name;
int statRet = stat(filepath.c_str(), &st);
if (!S_ISDIR(st.st_mode)) continue;
if (statRet != -1) {
sections.push_back((string)dptr->d_name);
linklist ll;
links.push_back(ll);
}
}
closedir(dirp);
sort(sections.begin(),sections.end(),case_less());
setSectionIndex(0);
readLinks();
}
Menu::~Menu() {
freeLinks();
}
uint Menu::firstDispRow() {
return iFirstDispRow;
}
void Menu::loadIcons() {
//reload section icons
for (uint i=0; i<sections.size(); i++) {
string sectionIcon = "sections/"+sections[i]+".png";
if (!gmenu2x->sc.getSkinFilePath(sectionIcon).empty())
gmenu2x->sc.add("skin:"+sectionIcon);
//check link's icons
string linkIcon;
for (uint x=0; x<sectionLinks(i)->size(); x++) {
linkIcon = sectionLinks(i)->at(x)->getIcon();
LinkApp *linkapp = dynamic_cast<LinkApp*>(sectionLinks(i)->at(x));
if (linkIcon.substr(0,5)=="skin:") {
linkIcon = gmenu2x->sc.getSkinFilePath(linkIcon.substr(5,linkIcon.length()));
if (linkapp != NULL && !fileExists(linkIcon))
linkapp->searchIcon();
else
sectionLinks(i)->at(x)->setIconPath(linkIcon);
} else if (!fileExists(linkIcon)) {
if (linkapp != NULL) linkapp->searchIcon();
}
}
}
}
/*====================================
SECTION MANAGEMENT
====================================*/
void Menu::freeLinks() {
for (vector<linklist>::iterator section = links.begin(); section<links.end(); section++)
for (linklist::iterator link = section->begin(); link<section->end(); link++)
free(*link);
}
linklist *Menu::sectionLinks(int i) {
if (i<0 || i>(int)links.size())
i = selSectionIndex();
if (i<0 || i>(int)links.size())
return NULL;
return &links[i];
}
void Menu::decSectionIndex() {
setSectionIndex(iSection-1);
}
void Menu::incSectionIndex() {
setSectionIndex(iSection+1);
}
uint Menu::firstDispSection() {
return iFirstDispSection;
}
int Menu::selSectionIndex() {
return iSection;
}
const string &Menu::selSection() {
return sections[iSection];
}
void Menu::setSectionIndex(int i) {
if (i<0)
i=sections.size()-1;
else if (i>=(int)sections.size())
i=0;
iSection = i;
if (i>(int)iFirstDispSection+4)
iFirstDispSection = i-4;
else if (i<(int)iFirstDispSection)
iFirstDispSection = i;
iLink = 0;
iFirstDispRow = 0;
}
string Menu::sectionPath(int section) {
if (section<0 || section>(int)sections.size()) section = iSection;
return "sections/"+sections[section]+"/";
}
/*====================================
LINKS MANAGEMENT
====================================*/
bool Menu::addActionLink(uint section, const string &title, LinkRunAction action, const string &description, const string &icon) {
if (section>=sections.size()) return false;
LinkAction *linkact = new LinkAction(gmenu2x,action);
linkact->setSize(gmenu2x->skinConfInt["linkWidth"],gmenu2x->skinConfInt["linkHeight"]);
linkact->setTitle(title);
linkact->setDescription(description);
if (gmenu2x->sc.exists(icon) || (icon.substr(0,5)=="skin:" && !gmenu2x->sc.getSkinFilePath(icon.substr(5,icon.length())).empty()) || fileExists(icon))
linkact->setIcon(icon);
sectionLinks(section)->push_back(linkact);
return true;
}
bool Menu::addLink(string path, string file, string section) {
if (section=="")
section = selSection();
else if (find(sections.begin(),sections.end(),section)==sections.end()) {
//section directory doesn't exists
if (!addSection(section))
return false;
}
//if the extension is not equal to gpu or dge then enable the wrapepr by default
bool wrapper = false;
//strip the extension from the filename
string title = file;
string::size_type pos = title.rfind(".");
if (pos!=string::npos && pos>0) {
string ext = title.substr(pos, title.length());
transform(ext.begin(), ext.end(), ext.begin(), (int(*)(int)) tolower);
if (ext == ".gpu" || ext == ".dge") wrapper = false;
title = title.substr(0, pos);
}
string linkpath = "sections/"+section+"/"+title;
int x=2;
while (fileExists(linkpath)) {
stringstream ss;
linkpath = "";
ss << x;
ss >> linkpath;
linkpath = "sections/"+section+"/"+title+linkpath;
x++;
}
#ifdef DEBUG
cout << "\033[0;34mGMENU2X:\033[0m Adding link: " << linkpath << endl;
#endif
if (path[path.length()-1]!='/') path += "/";
//search for a manual
pos = file.rfind(".");
string exename = path+file.substr(0,pos);
string manual = "";
if (fileExists(exename+".man.png")) {
manual = exename+".man.png";
} else if (fileExists(exename+".man.jpg")) {
manual = exename+".man.jpg";
} else if (fileExists(exename+".man.jpeg")) {
manual = exename+".man.jpeg";
} else if (fileExists(exename+".man.bmp")) {
manual = exename+".man.bmp";
} else if (fileExists(exename+".man.txt")) {
manual = exename+".man.txt";
} else {
//scan directory for a file like *readme*
FileLister fl(path, false);
fl.setFilter(".txt");
fl.browse();
bool found = false;
for (uint x=0; x<fl.size() && !found; x++) {
string lcfilename = fl[x];
if (lcfilename.find("readme") != string::npos) {
found = true;
manual = path+fl.files[x];
}
}
}
#ifdef DEBUG
cout << "\033[0;34mGMENU2X:\033[0m Manual: " << manual << endl;
#endif
string shorttitle=title, description="", exec=path+file, icon="";
//Reduce title lenght to fit the link width
if (gmenu2x->font->getTextWidth(shorttitle)>gmenu2x->skinConfInt["linkWidth"]) {
while (gmenu2x->font->getTextWidth(shorttitle+"..")>gmenu2x->skinConfInt["linkWidth"])
shorttitle = shorttitle.substr(0,shorttitle.length()-1);
shorttitle += "..";
}
ofstream f(linkpath.c_str());
if (f.is_open()) {
f << "title=" << shorttitle << endl;
f << "exec=" << exec << endl;
if (!description.empty()) f << "description=" << description << endl;
if (!icon.empty()) f << "icon=" << icon << endl;
if (!manual.empty()) f << "manual=" << manual << endl;
if (wrapper) f << "wrapper=true" << endl;
f.close();
sync();
int isection = find(sections.begin(),sections.end(),section) - sections.begin();
if (isection>=0 && isection<(int)sections.size()) {
#ifdef DEBUG
cout << "\033[0;34mGMENU2X:\033[0m Section: " << sections[isection] << "(" << isection << ")" << endl;
#endif
LinkApp* link = new LinkApp(gmenu2x, linkpath.c_str());
link->setSize(gmenu2x->skinConfInt["linkWidth"],gmenu2x->skinConfInt["linkHeight"]);
links[isection].push_back( link );
}
} else {
#ifdef DEBUG
cout << "\033[0;34mGMENU2X:\033[0;31m Error while opening the file '" << linkpath << "' for write\033[0m" << endl;
#endif
return false;
}
return true;
}
bool Menu::addSection(const string &sectionName) {
string sectiondir = "sections/"+sectionName;
if (mkdir(sectiondir.c_str(),0777)==0) {
sections.push_back(sectionName);
linklist ll;
links.push_back(ll);
return true;
}
return false;
}
void Menu::deleteSelectedLink() {
#ifdef DEBUG
cout << "\033[0;34mGMENU2X:\033[0m Deleting link " << selLink()->getTitle() << endl;
#endif
if (selLinkApp()!=NULL)
unlink(selLinkApp()->file.c_str());
gmenu2x->sc.del(selLink()->getIconPath());
sectionLinks()->erase( sectionLinks()->begin() + selLinkIndex() );
setLinkIndex(selLinkIndex());
}
void Menu::deleteSelectedSection() {
#ifdef DEBUG
cout << "\033[0;34mGMENU2X:\033[0m Deleting section " << selSection() << endl;
#endif
gmenu2x->sc.del("sections/"+selSection()+".png");
links.erase( links.begin()+selSectionIndex() );
sections.erase( sections.begin()+selSectionIndex() );
setSectionIndex(0); //reload sections
}
bool Menu::linkChangeSection(uint linkIndex, uint oldSectionIndex, uint newSectionIndex) {
if (oldSectionIndex<sections.size() && newSectionIndex<sections.size() && linkIndex<sectionLinks(oldSectionIndex)->size()) {
sectionLinks(newSectionIndex)->push_back( sectionLinks(oldSectionIndex)->at(linkIndex) );
sectionLinks(oldSectionIndex)->erase( sectionLinks(oldSectionIndex)->begin()+linkIndex );
//Select the same link in the new position
setSectionIndex(newSectionIndex);
setLinkIndex(sectionLinks(newSectionIndex)->size()-1);
return true;
}
return false;
}
void Menu::linkLeft() {
if (iLink%gmenu2x->linkColumns == 0)
setLinkIndex( sectionLinks()->size()>iLink+gmenu2x->linkColumns-1 ? iLink+gmenu2x->linkColumns-1 : sectionLinks()->size()-1 );
else
setLinkIndex(iLink-1);
}
void Menu::linkRight() {
if (iLink%gmenu2x->linkColumns == (gmenu2x->linkColumns-1) || iLink == (int)sectionLinks()->size()-1)
setLinkIndex(iLink-iLink%gmenu2x->linkColumns);
else
setLinkIndex(iLink+1);
}
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
void Menu::linkUp() {
int l = iLink-gmenu2x->linkColumns;
if (l<0) {
unsigned int rows;
rows = DIV_ROUND_UP(sectionLinks()->size(), gmenu2x->linkColumns);
l = (rows*gmenu2x->linkColumns)+l;
if (l >= (int)sectionLinks()->size())
l -= gmenu2x->linkColumns;
}
setLinkIndex(l);
}
void Menu::linkDown() {
uint l = iLink+gmenu2x->linkColumns;
if (l >= sectionLinks()->size()) {
unsigned int rows, curCol;
rows = DIV_ROUND_UP(sectionLinks()->size(), gmenu2x->linkColumns);
curCol = DIV_ROUND_UP(iLink + 1, gmenu2x->linkColumns);
if (rows > curCol)
l = sectionLinks()->size()-1;
else
l %= gmenu2x->linkColumns;
}
setLinkIndex(l);
}
int Menu::selLinkIndex() {
return iLink;
}
Link *Menu::selLink() {
if (sectionLinks()->size()==0) return NULL;
return sectionLinks()->at(iLink);
}
LinkApp *Menu::selLinkApp() {
return dynamic_cast<LinkApp*>(selLink());
}
void Menu::setLinkIndex(int i) {
if (i<0)
i=sectionLinks()->size()-1;
else if (i>=(int)sectionLinks()->size())
i=0;
if (i>=(int)(iFirstDispRow*gmenu2x->linkColumns+gmenu2x->linkColumns*gmenu2x->linkRows))
iFirstDispRow = i/gmenu2x->linkColumns-gmenu2x->linkRows+1;
else if (i<(int)(iFirstDispRow*gmenu2x->linkColumns))
iFirstDispRow = i/gmenu2x->linkColumns;
iLink = i;
}
void Menu::readLinks() {
vector<string> linkfiles;
iLink = 0;
iFirstDispRow = 0;
DIR *dirp;
struct stat st;
struct dirent *dptr;
string filepath;
for (uint i=0; i<links.size(); i++) {
links[i].clear();
linkfiles.clear();
if ((dirp = opendir(sectionPath(i).c_str())) == NULL) continue;
while ((dptr = readdir(dirp))) {
if (dptr->d_name[0]=='.') continue;
filepath = sectionPath(i)+dptr->d_name;
int statRet = stat(filepath.c_str(), &st);
if (S_ISDIR(st.st_mode)) continue;
if (statRet != -1) {
linkfiles.push_back(filepath);
}
}
sort(linkfiles.begin(), linkfiles.end(),case_less());
for (uint x=0; x<linkfiles.size(); x++) {
LinkApp *link = new LinkApp(gmenu2x, linkfiles[x].c_str());
link->setSize(gmenu2x->skinConfInt["linkWidth"],gmenu2x->skinConfInt["linkHeight"]);
if (link->targetExists())
links[i].push_back( link );
else
free(link);
}
closedir(dirp);
}
}