Root/src/menu.cpp

1/***************************************************************************
2 * Copyright (C) 2006 by Massimiliano Torromeo *
3 * massimiliano.torromeo@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#include <sstream>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <dirent.h>
25#include <algorithm>
26#include <math.h>
27#include <fstream>
28
29#include "gmenu2x.h"
30#include "linkapp.h"
31#include "menu.h"
32#include "filelister.h"
33#include "utilities.h"
34#include "debug.h"
35
36using namespace std;
37
38Menu::Menu(GMenu2X *gmenu2x) {
39    this->gmenu2x = gmenu2x;
40    iFirstDispSection = 0;
41
42    readSections(GMENU2X_SYSTEM_DIR "/sections");
43    readSections(GMenu2X::getHome() + "/sections");
44
45    sort(sections.begin(),sections.end(),case_less());
46    setSectionIndex(0);
47    readLinks();
48}
49
50Menu::~Menu() {
51    freeLinks();
52}
53
54uint Menu::firstDispRow() {
55    return iFirstDispRow;
56}
57
58void Menu::readSections(std::string parentDir)
59{
60    DIR *dirp;
61    struct stat st;
62    struct dirent *dptr;
63
64    dirp = opendir(parentDir.c_str());
65    if (!dirp) return;
66
67    while ((dptr = readdir(dirp))) {
68        int statret;
69        if (dptr->d_name[0]=='.') continue;
70
71        string filepath = parentDir + "/" + dptr->d_name;
72        statret = stat(filepath.c_str(), &st);
73        if (!S_ISDIR(st.st_mode)) continue;
74        if (statret != -1) {
75            if (find(sections.begin(), sections.end(), (string)dptr->d_name) == sections.end()) {
76                sections.push_back((string)dptr->d_name);
77                linklist ll;
78                links.push_back(ll);
79            }
80        }
81    }
82
83    closedir(dirp);
84}
85
86void Menu::loadIcons() {
87    //reload section icons
88    for (uint i=0; i<sections.size(); i++) {
89        string sectionIcon = "sections/"+sections[i]+".png";
90        if (!gmenu2x->sc.getSkinFilePath(sectionIcon).empty())
91            gmenu2x->sc.add("skin:"+sectionIcon);
92
93        //check link's icons
94        string linkIcon;
95        for (uint x=0; x<sectionLinks(i)->size(); x++) {
96            linkIcon = sectionLinks(i)->at(x)->getIcon();
97            LinkApp *linkapp = dynamic_cast<LinkApp*>(sectionLinks(i)->at(x));
98
99            if (linkIcon.substr(0,5)=="skin:") {
100                linkIcon = gmenu2x->sc.getSkinFilePath(linkIcon.substr(5,linkIcon.length()));
101                if (linkapp != NULL && !fileExists(linkIcon))
102                    linkapp->searchIcon();
103                else
104                    sectionLinks(i)->at(x)->setIconPath(linkIcon);
105
106            } else if (!fileExists(linkIcon)) {
107                if (linkapp != NULL) linkapp->searchIcon();
108            }
109        }
110    }
111}
112
113/*====================================
114   SECTION MANAGEMENT
115  ====================================*/
116void Menu::freeLinks() {
117    for (vector<linklist>::iterator section = links.begin(); section<links.end(); section++)
118        for (linklist::iterator link = section->begin(); link<section->end(); link++)
119            free(*link);
120}
121
122linklist *Menu::sectionLinks(int i) {
123    if (i<0 || i>(int)links.size())
124        i = selSectionIndex();
125
126    if (i<0 || i>(int)links.size())
127        return NULL;
128
129    return &links[i];
130}
131
132void Menu::decSectionIndex() {
133    setSectionIndex(iSection-1);
134}
135
136void Menu::incSectionIndex() {
137    setSectionIndex(iSection+1);
138}
139
140uint Menu::firstDispSection() {
141    return iFirstDispSection;
142}
143
144int Menu::selSectionIndex() {
145    return iSection;
146}
147
148const string &Menu::selSection() {
149    return sections[iSection];
150}
151
152void Menu::setSectionIndex(int i) {
153    if (i<0)
154        i=sections.size()-1;
155    else if (i>=(int)sections.size())
156        i=0;
157    iSection = i;
158
159    if (i>(int)iFirstDispSection+2)
160        iFirstDispSection = i-2;
161    else if (i<(int)iFirstDispSection)
162        iFirstDispSection = i;
163
164    iLink = 0;
165    iFirstDispRow = 0;
166}
167
168/*====================================
169   LINKS MANAGEMENT
170  ====================================*/
171bool Menu::addActionLink(uint section, const string &title, LinkRunAction action, const string &description, const string &icon) {
172    if (section>=sections.size()) return false;
173
174    LinkAction *linkact = new LinkAction(gmenu2x,action);
175    linkact->setSize(gmenu2x->skinConfInt["linkWidth"],gmenu2x->skinConfInt["linkHeight"]);
176    linkact->setTitle(title);
177    linkact->setDescription(description);
178    if (gmenu2x->sc.exists(icon) || (icon.substr(0,5)=="skin:" && !gmenu2x->sc.getSkinFilePath(icon.substr(5,icon.length())).empty()) || fileExists(icon))
179    linkact->setIcon(icon);
180
181    sectionLinks(section)->push_back(linkact);
182    return true;
183}
184
185bool Menu::addLink(string path, string file, string section) {
186    if (section=="")
187        section = selSection();
188    else if (find(sections.begin(),sections.end(),section)==sections.end()) {
189        //section directory doesn't exists
190        if (!addSection(section))
191            return false;
192    }
193
194    //if the extension is not equal to gpu or dge then enable the wrapper by default
195    bool wrapper = false;
196
197    //strip the extension from the filename
198    string title = file;
199    string::size_type pos = title.rfind(".");
200    if (pos!=string::npos && pos>0) {
201        string ext = title.substr(pos, title.length());
202        transform(ext.begin(), ext.end(), ext.begin(), (int(*)(int)) tolower);
203        if (ext == ".gpu" || ext == ".dge") wrapper = false;
204        title = title.substr(0, pos);
205    }
206
207    string linkpath = GMenu2X::getHome()+"/sections/"+section;
208    if (!fileExists(linkpath))
209        mkdir(linkpath.c_str(), 0755);
210
211    linkpath += "/" + title;
212    int x=2;
213    while (fileExists(linkpath)) {
214        stringstream ss;
215        linkpath = "";
216        ss << x;
217        ss >> linkpath;
218        linkpath = GMenu2X::getHome()+"/sections/"+section+"/"+title+linkpath;
219        x++;
220    }
221
222    INFO("Adding link: '%s'\n", linkpath.c_str());
223
224    if (path[path.length()-1]!='/') path += "/";
225    //search for a manual
226    pos = file.rfind(".");
227    string exename = path+file.substr(0,pos);
228    string manual = "";
229    if (fileExists(exename+".man.png")) {
230        manual = exename+".man.png";
231    } else if (fileExists(exename+".man.jpg")) {
232        manual = exename+".man.jpg";
233    } else if (fileExists(exename+".man.jpeg")) {
234        manual = exename+".man.jpeg";
235    } else if (fileExists(exename+".man.bmp")) {
236        manual = exename+".man.bmp";
237    } else if (fileExists(exename+".man.txt")) {
238        manual = exename+".man.txt";
239    } else {
240        //scan directory for a file like *readme*
241        FileLister fl(path, false);
242        fl.setFilter(".txt");
243        fl.browse();
244        bool found = false;
245        for (uint x=0; x<fl.size() && !found; x++) {
246            string lcfilename = fl[x];
247
248            if (lcfilename.find("readme") != string::npos) {
249                found = true;
250                manual = path+fl.getFiles()[x];
251            }
252        }
253    }
254
255    INFO("Manual: '%s'\n", manual.c_str());
256
257    string shorttitle=title, description="", exec=path+file, icon="";
258    if (fileExists(exename+".png")) icon = exename+".png";
259
260    //Reduce title lenght to fit the link width
261    if (gmenu2x->font->getTextWidth(shorttitle)>gmenu2x->skinConfInt["linkWidth"]) {
262        while (gmenu2x->font->getTextWidth(shorttitle+"..")>gmenu2x->skinConfInt["linkWidth"])
263            shorttitle = shorttitle.substr(0,shorttitle.length()-1);
264        shorttitle += "..";
265    }
266
267    ofstream f(linkpath.c_str());
268    if (f.is_open()) {
269        f << "title=" << shorttitle << endl;
270        f << "exec=" << exec << endl;
271        if (!description.empty()) f << "description=" << description << endl;
272        if (!icon.empty()) f << "icon=" << icon << endl;
273        if (!manual.empty()) f << "manual=" << manual << endl;
274        if (wrapper) f << "wrapper=true" << endl;
275        f.close();
276         sync();
277        int isection = find(sections.begin(),sections.end(),section) - sections.begin();
278        if (isection>=0 && isection<(int)sections.size()) {
279
280            INFO("Section: '%s(%i)'\n", sections[isection].c_str(), isection);
281
282            LinkApp* link = new LinkApp(gmenu2x, gmenu2x->input, linkpath.c_str());
283            link->setSize(gmenu2x->skinConfInt["linkWidth"],gmenu2x->skinConfInt["linkHeight"]);
284            links[isection].push_back( link );
285        }
286    } else {
287
288        ERROR("Error while opening the file '%s' for write.\n", linkpath.c_str());
289
290        return false;
291    }
292
293    return true;
294}
295
296bool Menu::addSection(const string &sectionName) {
297    string sectiondir = GMenu2X::getHome() + "/sections";
298    if (!fileExists(sectiondir))
299        mkdir(sectiondir.c_str(), 0755);
300
301    sectiondir = sectiondir + "/" + sectionName;
302    if (mkdir(sectiondir.c_str(), 0755) == 0) {
303        sections.push_back(sectionName);
304        linklist ll;
305        links.push_back(ll);
306        return true;
307    }
308    return false;
309}
310
311void Menu::deleteSelectedLink() {
312    INFO("Deleting link '%s'\n", selLink()->getTitle().c_str());
313
314    if (selLinkApp()!=NULL)
315        unlink(selLinkApp()->getFile().c_str());
316    gmenu2x->sc.del(selLink()->getIconPath());
317    sectionLinks()->erase( sectionLinks()->begin() + selLinkIndex() );
318    setLinkIndex(selLinkIndex());
319}
320
321void Menu::deleteSelectedSection() {
322    INFO("Deleting section '%s'\n", selSection().c_str());
323
324    gmenu2x->sc.del("sections/"+selSection()+".png");
325    links.erase( links.begin()+selSectionIndex() );
326    sections.erase( sections.begin()+selSectionIndex() );
327    setSectionIndex(0); //reload sections
328}
329
330bool Menu::linkChangeSection(uint linkIndex, uint oldSectionIndex, uint newSectionIndex) {
331    if (oldSectionIndex<sections.size() && newSectionIndex<sections.size() && linkIndex<sectionLinks(oldSectionIndex)->size()) {
332        sectionLinks(newSectionIndex)->push_back( sectionLinks(oldSectionIndex)->at(linkIndex) );
333        sectionLinks(oldSectionIndex)->erase( sectionLinks(oldSectionIndex)->begin()+linkIndex );
334        //Select the same link in the new position
335        setSectionIndex(newSectionIndex);
336        setLinkIndex(sectionLinks(newSectionIndex)->size()-1);
337        return true;
338    }
339    return false;
340}
341
342void Menu::linkLeft() {
343    if (iLink%gmenu2x->linkColumns == 0)
344        setLinkIndex( sectionLinks()->size()>iLink+gmenu2x->linkColumns-1 ? iLink+gmenu2x->linkColumns-1 : sectionLinks()->size()-1 );
345    else
346        setLinkIndex(iLink-1);
347}
348
349void Menu::linkRight() {
350    if (iLink%gmenu2x->linkColumns == (gmenu2x->linkColumns-1) || iLink == (int)sectionLinks()->size()-1)
351        setLinkIndex(iLink-iLink%gmenu2x->linkColumns);
352    else
353        setLinkIndex(iLink+1);
354}
355
356#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
357
358void Menu::linkUp() {
359    int l = iLink-gmenu2x->linkColumns;
360    if (l<0) {
361        unsigned int rows;
362        rows = DIV_ROUND_UP(sectionLinks()->size(), gmenu2x->linkColumns);
363        l = (rows*gmenu2x->linkColumns)+l;
364        if (l >= (int)sectionLinks()->size())
365            l -= gmenu2x->linkColumns;
366    }
367    setLinkIndex(l);
368}
369
370void Menu::linkDown() {
371    uint l = iLink+gmenu2x->linkColumns;
372    if (l >= sectionLinks()->size()) {
373        unsigned int rows, curCol;
374        rows = DIV_ROUND_UP(sectionLinks()->size(), gmenu2x->linkColumns);
375        curCol = DIV_ROUND_UP(iLink + 1, gmenu2x->linkColumns);
376        if (rows > curCol)
377            l = sectionLinks()->size()-1;
378        else
379            l %= gmenu2x->linkColumns;
380    }
381    setLinkIndex(l);
382}
383
384int Menu::selLinkIndex() {
385    return iLink;
386}
387
388Link *Menu::selLink() {
389    if (sectionLinks()->size()==0) return NULL;
390    return sectionLinks()->at(iLink);
391}
392
393LinkApp *Menu::selLinkApp() {
394    return dynamic_cast<LinkApp*>(selLink());
395}
396
397void Menu::setLinkIndex(int i) {
398    if (i<0)
399        i=sectionLinks()->size()-1;
400    else if (i>=(int)sectionLinks()->size())
401        i=0;
402
403    if (i>=(int)(iFirstDispRow*gmenu2x->linkColumns+gmenu2x->linkColumns*gmenu2x->linkRows))
404        iFirstDispRow = i/gmenu2x->linkColumns-gmenu2x->linkRows+1;
405    else if (i<(int)(iFirstDispRow*gmenu2x->linkColumns))
406        iFirstDispRow = i/gmenu2x->linkColumns;
407
408    iLink = i;
409}
410
411void Menu::readLinksOfSection(std::string path, std::vector<std::string> &linkfiles)
412{
413    DIR *dirp;
414    struct stat st;
415    struct dirent *dptr;
416
417    if ((dirp = opendir(path.c_str())) == NULL) return;
418
419    while ((dptr = readdir(dirp))) {
420        if (dptr->d_name[0] == '.') continue;
421        string filepath = path + "/" + dptr->d_name;
422        int statret = stat(filepath.c_str(), &st);
423        if (S_ISDIR(st.st_mode)) continue;
424        if (statret != -1)
425            linkfiles.push_back(filepath);
426    }
427
428    closedir(dirp);
429}
430
431void Menu::readLinks() {
432    vector<string> linkfiles;
433
434    iLink = 0;
435    iFirstDispRow = 0;
436    string filepath;
437
438    for (uint i=0; i<links.size(); i++) {
439        links[i].clear();
440        linkfiles.clear();
441
442        int correct = (i>sections.size() ? iSection : i);
443
444        readLinksOfSection(GMENU2X_SYSTEM_DIR "/sections/"
445          + sections[correct], linkfiles);
446
447        readLinksOfSection(GMenu2X::getHome() + "/sections/"
448          + sections[correct], linkfiles);
449
450        sort(linkfiles.begin(), linkfiles.end(),case_less());
451        for (uint x=0; x<linkfiles.size(); x++) {
452            LinkApp *link = new LinkApp(gmenu2x, gmenu2x->input, linkfiles[x].c_str());
453            link->setSize(gmenu2x->skinConfInt["linkWidth"],gmenu2x->skinConfInt["linkHeight"]);
454            if (link->targetExists())
455                links[i].push_back( link );
456            else
457                free(link);
458        }
459    }
460}
461
462void Menu::renameSection(int index, const string &name) {
463    sections[index] = name;
464}
465

Archive Download this file



interactive