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#include <unistd.h>
29
30#ifdef HAVE_LIBOPK
31#include <opk.h>
32#endif
33
34#include "gmenu2x.h"
35#include "linkapp.h"
36#include "menu.h"
37#include "monitor.h"
38#include "filelister.h"
39#include "utilities.h"
40#include "debug.h"
41#include "iconbutton.h"
42
43using namespace std;
44
45
46Menu::Animation::Animation()
47    : curr(0)
48{
49}
50
51void Menu::Animation::adjust(int delta)
52{
53    curr += delta;
54}
55
56void Menu::Animation::step()
57{
58    if (curr == 0) {
59        ERROR("Computing step past animation end\n");
60    } else if (curr < 0) {
61        const int v = ((1 << 16) - curr) / 32;
62        curr = std::min(0, curr + v);
63    } else {
64        const int v = ((1 << 16) + curr) / 32;
65        curr = std::max(0, curr - v);
66    }
67}
68
69Menu::Menu(GMenu2X *gmenu2x, Touchscreen &ts)
70    : gmenu2x(gmenu2x)
71    , ts(ts)
72    , btnContextMenu(new IconButton(gmenu2x, ts, "skin:imgs/menu.png"))
73{
74    readSections(GMENU2X_SYSTEM_DIR "/sections");
75    readSections(GMenu2X::getHome() + "/sections");
76
77    sort(sections.begin(),sections.end(),case_less());
78    setSectionIndex(0);
79    readLinks();
80
81#ifdef HAVE_LIBOPK
82    {
83        struct dirent *dptr;
84        DIR *dirp = opendir(CARD_ROOT);
85        if (dirp) {
86            while ((dptr = readdir(dirp))) {
87                if (dptr->d_type != DT_DIR)
88                    continue;
89
90                if (!strcmp(dptr->d_name, ".") || !strcmp(dptr->d_name, ".."))
91                    continue;
92
93                openPackagesFromDir((string) CARD_ROOT + "/" +
94                            dptr->d_name + "/apps");
95            }
96            closedir(dirp);
97        }
98    }
99#endif
100
101    orderLinks();
102
103    btnContextMenu->setPosition(gmenu2x->resX - 38, gmenu2x->bottomBarIconY);
104    btnContextMenu->setAction(std::bind(&GMenu2X::showContextMenu, gmenu2x));
105}
106
107Menu::~Menu() {
108    freeLinks();
109
110#ifdef ENABLE_INOTIFY
111    for (auto it : monitors)
112        delete it;
113#endif
114}
115
116void Menu::readSections(std::string parentDir)
117{
118    DIR *dirp;
119    struct dirent *dptr;
120
121    dirp = opendir(parentDir.c_str());
122    if (!dirp) return;
123
124    while ((dptr = readdir(dirp))) {
125        if (dptr->d_name[0] == '.' || dptr->d_type != DT_DIR)
126            continue;
127
128        string filepath = parentDir + "/" + dptr->d_name;
129
130        if (find(sections.begin(), sections.end(), (string)dptr->d_name) == sections.end()) {
131            sections.push_back((string)dptr->d_name);
132            vector<Link*> ll;
133            links.push_back(ll);
134        }
135    }
136
137    closedir(dirp);
138}
139
140void Menu::skinUpdated() {
141    ConfIntHash &skinConfInt = gmenu2x->skinConfInt;
142
143    //recalculate some coordinates based on the new element sizes
144    linkColumns = (gmenu2x->resX - 10) / skinConfInt["linkWidth"];
145    linkRows = (gmenu2x->resY - 35 - skinConfInt["topBarHeight"]) / skinConfInt["linkHeight"];
146
147    //reload section icons
148    vector<string>::size_type i = 0;
149    for (string sectionName : sections) {
150        string sectionIcon = "sections/" + sectionName + ".png";
151        if (!gmenu2x->sc.getSkinFilePath(sectionIcon).empty())
152            gmenu2x->sc.add("skin:" + sectionIcon);
153
154        for (Link *&link : links[i]) {
155            link->loadIcon();
156        }
157
158        i++;
159    }
160}
161
162void Menu::calcSectionRange(int &leftSection, int &rightSection) {
163    ConfIntHash &skinConfInt = gmenu2x->skinConfInt;
164    const int linkWidth = skinConfInt["linkWidth"];
165    const int screenWidth = gmenu2x->resX;
166    const int numSections = sections.size();
167    rightSection = min(
168            max(1, (screenWidth - 20 - linkWidth) / (2 * linkWidth)),
169            numSections / 2);
170    leftSection = max(
171            -rightSection,
172            rightSection - numSections + 1);
173}
174
175bool Menu::runAnimations() {
176    if (sectionAnimation.isRunning()) {
177        sectionAnimation.step();
178    }
179    return sectionAnimation.isRunning();
180}
181
182void Menu::paint(Surface &s) {
183    const uint width = s.width(), height = s.height();
184    Font &font = *gmenu2x->font;
185    SurfaceCollection &sc = gmenu2x->sc;
186
187    ConfIntHash &skinConfInt = gmenu2x->skinConfInt;
188    const int topBarHeight = skinConfInt["topBarHeight"];
189    const int bottomBarHeight = skinConfInt["bottomBarHeight"];
190    const int linkWidth = skinConfInt["linkWidth"];
191    const int linkHeight = skinConfInt["linkHeight"];
192    RGBAColor &selectionBgColor = gmenu2x->skinConfColors[COLOR_SELECTION_BG];
193
194    // Apply section header animation.
195    int leftSection, rightSection;
196    calcSectionRange(leftSection, rightSection);
197    int sectionFP = sectionAnimation.currentValue();
198    int sectionDelta = (sectionFP * linkWidth + (1 << 15)) >> 16;
199    int centerSection = iSection - sectionDelta / linkWidth;
200    sectionDelta %= linkWidth;
201    if (sectionDelta < 0) {
202        rightSection++;
203    } else if (sectionDelta > 0) {
204        leftSection--;
205    }
206
207    // Paint section headers.
208    s.box(width / 2 - linkWidth / 2, 0, linkWidth, topBarHeight, selectionBgColor);
209    const uint sectionLinkPadding = (topBarHeight - 32 - font.getHeight()) / 3;
210    const uint numSections = sections.size();
211    for (int i = leftSection; i <= rightSection; i++) {
212        uint j = (centerSection + numSections + i) % numSections;
213        string sectionIcon = "skin:sections/" + sections[j] + ".png";
214        Surface *icon = sc.exists(sectionIcon)
215                ? sc[sectionIcon]
216                : sc.skinRes("icons/section.png");
217        int x = width / 2 + i * linkWidth + sectionDelta;
218        if (i == leftSection) {
219            int t = sectionDelta > 0 ? linkWidth - sectionDelta : -sectionDelta;
220            x -= (((t * t) / linkWidth) * t) / linkWidth;
221        } else if (i == rightSection) {
222            int t = sectionDelta < 0 ? sectionDelta + linkWidth : sectionDelta;
223            x += (((t * t) / linkWidth) * t) / linkWidth;
224        }
225        icon->blit(&s, x - 16, sectionLinkPadding, 32, 32);
226        s.write(&font, sections[j], x, topBarHeight - sectionLinkPadding,
227                Font::HAlignCenter, Font::VAlignBottom);
228    }
229    sc.skinRes("imgs/section-l.png")->blit(&s, 0, 0);
230    sc.skinRes("imgs/section-r.png")->blit(&s, width - 10, 0);
231
232    vector<Link*> &sectionLinks = links[iSection];
233    const uint numLinks = sectionLinks.size();
234    gmenu2x->drawScrollBar(
235            linkRows, (numLinks + linkColumns - 1) / linkColumns, iFirstDispRow);
236
237    //Links
238    const uint linksPerPage = linkColumns * linkRows;
239    const int linkSpacingX = (width - 10 - linkColumns * linkWidth) / linkColumns;
240    const int linkMarginX = (
241            width - linkWidth * linkColumns - linkSpacingX * (linkColumns - 1)
242            ) / 2;
243    const int linkSpacingY = (height - 35 - topBarHeight - linkRows * linkHeight) / linkRows;
244    for (uint i = iFirstDispRow * linkColumns; i < iFirstDispRow * linkColumns + linksPerPage && i < numLinks; i++) {
245        const int ir = i - iFirstDispRow * linkColumns;
246        const int x = linkMarginX + (ir % linkColumns) * (linkWidth + linkSpacingX);
247        const int y = ir / linkColumns * (linkHeight + linkSpacingY) + topBarHeight + 2;
248        sectionLinks.at(i)->setPosition(x, y);
249
250        if (i == (uint)iLink) {
251            sectionLinks.at(i)->paintHover();
252        }
253
254        sectionLinks.at(i)->paint();
255    }
256
257    if (selLink()) {
258        s.write(&font, selLink()->getDescription(),
259                width / 2, height - bottomBarHeight + 2,
260                Font::HAlignCenter, Font::VAlignBottom);
261    }
262
263    LinkApp *linkApp = selLinkApp();
264    if (linkApp) {
265#ifdef ENABLE_CPUFREQ
266        s.write(&font, linkApp->clockStr(gmenu2x->confInt["maxClock"]),
267                gmenu2x->cpuX, gmenu2x->bottomBarTextY,
268                Font::HAlignLeft, Font::VAlignMiddle);
269#endif
270        //Manual indicator
271        if (!linkApp->getManual().empty())
272            sc.skinRes("imgs/manual.png")->blit(
273                    &s, gmenu2x->manualX, gmenu2x->bottomBarIconY);
274    }
275
276    if (ts.available()) {
277        btnContextMenu->paint();
278    }
279}
280
281bool Menu::handleButtonPress(InputManager::Button button) {
282    switch (button) {
283        case InputManager::ACCEPT:
284            if (selLink() != NULL) selLink()->run();
285            return true;
286        case InputManager::UP:
287            linkUp();
288            return true;
289        case InputManager::DOWN:
290            linkDown();
291            return true;
292        case InputManager::LEFT:
293            linkLeft();
294            return true;
295        case InputManager::RIGHT:
296            linkRight();
297            return true;
298        case InputManager::ALTLEFT:
299            decSectionIndex();
300            return true;
301        case InputManager::ALTRIGHT:
302            incSectionIndex();
303            return true;
304        case InputManager::MENU:
305            gmenu2x->showContextMenu();
306            return true;
307        default:
308            return false;
309    }
310}
311
312bool Menu::handleTouchscreen(Touchscreen &ts) {
313    btnContextMenu->handleTS();
314
315    ConfIntHash &skinConfInt = gmenu2x->skinConfInt;
316    const int topBarHeight = skinConfInt["topBarHeight"];
317    const int screenWidth = gmenu2x->resX;
318
319    if (ts.pressed() && ts.getY() < topBarHeight) {
320        int leftSection, rightSection;
321        calcSectionRange(leftSection, rightSection);
322
323        const int linkWidth = skinConfInt["linkWidth"];
324        const int leftSectionX = screenWidth / 2 + leftSection * linkWidth;
325        const int i = min(
326                leftSection + max((ts.getX() - leftSectionX) / linkWidth, 0),
327                rightSection);
328        const uint numSections = sections.size();
329        setSectionIndex((iSection + numSections + i) % numSections);
330
331        ts.setHandled();
332        return true;
333    }
334
335    const uint linksPerPage = linkColumns * linkRows;
336    uint i = iFirstDispRow * linkColumns;
337    while (i < (iFirstDispRow * linkColumns) + linksPerPage && i < sectionLinks()->size()) {
338        if (sectionLinks()->at(i)->isPressed()) {
339            setLinkIndex(i);
340        }
341        if (sectionLinks()->at(i)->handleTS()) {
342            i = sectionLinks()->size();
343        }
344        i++;
345    }
346    return ts.handled();
347}
348
349/*====================================
350   SECTION MANAGEMENT
351  ====================================*/
352void Menu::freeLinks() {
353    for (vector< vector<Link*> >::iterator section = links.begin(); section<links.end(); section++)
354        for (vector<Link*>::iterator link = section->begin(); link<section->end(); link++)
355            delete *link;
356}
357
358vector<Link*> *Menu::sectionLinks(int i) {
359    if (i<0 || i>(int)links.size())
360        i = selSectionIndex();
361
362    if (i<0 || i>(int)links.size())
363        return NULL;
364
365    return &links[i];
366}
367
368void Menu::decSectionIndex() {
369    sectionAnimation.adjust(-1 << 16);
370    setSectionIndex(iSection - 1);
371}
372
373void Menu::incSectionIndex() {
374    sectionAnimation.adjust(1 << 16);
375    setSectionIndex(iSection + 1);
376}
377
378int Menu::selSectionIndex() {
379    return iSection;
380}
381
382const string &Menu::selSection() {
383    return sections[iSection];
384}
385
386void Menu::setSectionIndex(int i) {
387    if (i<0)
388        i=sections.size()-1;
389    else if (i>=(int)sections.size())
390        i=0;
391    iSection = i;
392
393    iLink = 0;
394    iFirstDispRow = 0;
395}
396
397/*====================================
398   LINKS MANAGEMENT
399  ====================================*/
400bool Menu::addActionLink(uint section, const string &title, function_t action, const string &description, const string &icon) {
401    if (section>=sections.size()) return false;
402
403    Link *link = new Link(gmenu2x, action);
404    link->setSize(gmenu2x->skinConfInt["linkWidth"], gmenu2x->skinConfInt["linkHeight"]);
405    link->setTitle(title);
406    link->setDescription(description);
407    if (gmenu2x->sc.exists(icon) || (icon.substr(0,5)=="skin:" && !gmenu2x->sc.getSkinFilePath(icon.substr(5,icon.length())).empty()) || fileExists(icon))
408    link->setIcon(icon);
409
410    sectionLinks(section)->push_back(link);
411    return true;
412}
413
414bool Menu::addLink(string path, string file, string section) {
415    if (section.empty()) {
416        section = selSection();
417    } else if (find(sections.begin(),sections.end(),section)==sections.end()) {
418        //section directory doesn't exists
419        if (!addSection(section))
420            return false;
421    }
422
423    //strip the extension from the filename
424    string title = file;
425    string::size_type pos = title.rfind(".");
426    if (pos!=string::npos && pos>0) {
427        string ext = title.substr(pos, title.length());
428        transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
429        title = title.substr(0, pos);
430    }
431
432    string linkpath = GMenu2X::getHome() + "/sections";
433    if (!fileExists(linkpath))
434        mkdir(linkpath.c_str(), 0755);
435
436    linkpath = GMenu2X::getHome() + "/sections/" + section;
437    if (!fileExists(linkpath))
438        mkdir(linkpath.c_str(), 0755);
439
440    linkpath += "/" + title;
441    int x=2;
442    while (fileExists(linkpath)) {
443        stringstream ss;
444        linkpath = "";
445        ss << x;
446        ss >> linkpath;
447        linkpath = GMenu2X::getHome()+"/sections/"+section+"/"+title+linkpath;
448        x++;
449    }
450
451    INFO("Adding link: '%s'\n", linkpath.c_str());
452
453    if (path[path.length()-1]!='/') path += "/";
454    //search for a manual
455    pos = file.rfind(".");
456    string exename = path+file.substr(0,pos);
457    string manual = "";
458    if (fileExists(exename+".man.png")) {
459        manual = exename+".man.png";
460    } else if (fileExists(exename+".man.txt")) {
461        manual = exename+".man.txt";
462    } else {
463        //scan directory for a file like *readme*
464        FileLister fl(path, false);
465        fl.setFilter(".txt");
466        fl.browse();
467        bool found = false;
468        for (uint x=0; x<fl.size() && !found; x++) {
469            string lcfilename = fl[x];
470
471            if (lcfilename.find("readme") != string::npos) {
472                found = true;
473                manual = path+fl.getFiles()[x];
474            }
475        }
476    }
477
478    INFO("Manual: '%s'\n", manual.c_str());
479
480    string shorttitle=title, description="", exec=path+file, icon="";
481    if (fileExists(exename+".png")) icon = exename+".png";
482
483    //Reduce title lenght to fit the link width
484    if (gmenu2x->font->getTextWidth(shorttitle)>gmenu2x->skinConfInt["linkWidth"]) {
485        while (gmenu2x->font->getTextWidth(shorttitle+"..")>gmenu2x->skinConfInt["linkWidth"])
486            shorttitle = shorttitle.substr(0,shorttitle.length()-1);
487        shorttitle += "..";
488    }
489
490    ofstream f(linkpath.c_str());
491    if (f.is_open()) {
492        f << "title=" << shorttitle << endl;
493        f << "exec=" << exec << endl;
494        if (!description.empty()) f << "description=" << description << endl;
495        if (!icon.empty()) f << "icon=" << icon << endl;
496        if (!manual.empty()) f << "manual=" << manual << endl;
497        f.close();
498         sync();
499        int isection = find(sections.begin(),sections.end(),section) - sections.begin();
500        if (isection>=0 && isection<(int)sections.size()) {
501
502            INFO("Section: '%s(%i)'\n", sections[isection].c_str(), isection);
503
504            LinkApp* link = new LinkApp(gmenu2x, linkpath.c_str());
505            link->setSize(gmenu2x->skinConfInt["linkWidth"],gmenu2x->skinConfInt["linkHeight"]);
506            links[isection].push_back( link );
507        }
508    } else {
509
510        ERROR("Error while opening the file '%s' for write.\n", linkpath.c_str());
511
512        return false;
513    }
514
515    return true;
516}
517
518bool Menu::addSection(const string &sectionName) {
519    string sectiondir = GMenu2X::getHome() + "/sections";
520    if (!fileExists(sectiondir))
521        mkdir(sectiondir.c_str(), 0755);
522
523    sectiondir = sectiondir + "/" + sectionName;
524    if (mkdir(sectiondir.c_str(), 0755) == 0) {
525        sections.push_back(sectionName);
526        vector<Link*> ll;
527        links.push_back(ll);
528        return true;
529    }
530    return false;
531}
532
533void Menu::deleteSelectedLink()
534{
535    bool icon_used = false;
536    string iconpath = selLink()->getIconPath();
537
538    INFO("Deleting link '%s'\n", selLink()->getTitle().c_str());
539
540    if (selLinkApp()!=NULL)
541        unlink(selLinkApp()->getFile().c_str());
542    sectionLinks()->erase( sectionLinks()->begin() + selLinkIndex() );
543    setLinkIndex(selLinkIndex());
544
545    for (vector< vector<Link*> >::iterator section = links.begin();
546                !icon_used && section<links.end(); section++)
547        for (vector<Link*>::iterator link = section->begin();
548                    !icon_used && link<section->end(); link++)
549            icon_used = !iconpath.compare((*link)->getIconPath());
550
551    if (!icon_used)
552      gmenu2x->sc.del(iconpath);
553}
554
555void Menu::deleteSelectedSection() {
556    INFO("Deleting section '%s'\n", selSection().c_str());
557
558    gmenu2x->sc.del("sections/"+selSection()+".png");
559    links.erase( links.begin()+selSectionIndex() );
560    sections.erase( sections.begin()+selSectionIndex() );
561    setSectionIndex(0); //reload sections
562}
563
564bool Menu::linkChangeSection(uint linkIndex, uint oldSectionIndex, uint newSectionIndex) {
565    if (oldSectionIndex<sections.size() && newSectionIndex<sections.size() && linkIndex<sectionLinks(oldSectionIndex)->size()) {
566        sectionLinks(newSectionIndex)->push_back( sectionLinks(oldSectionIndex)->at(linkIndex) );
567        sectionLinks(oldSectionIndex)->erase( sectionLinks(oldSectionIndex)->begin()+linkIndex );
568        //Select the same link in the new position
569        setSectionIndex(newSectionIndex);
570        setLinkIndex(sectionLinks(newSectionIndex)->size()-1);
571        return true;
572    }
573    return false;
574}
575
576void Menu::linkLeft() {
577    if (iLink % linkColumns == 0)
578        setLinkIndex(sectionLinks()->size() > iLink + linkColumns - 1
579                ? iLink + linkColumns - 1 : sectionLinks()->size() - 1);
580    else
581        setLinkIndex(iLink - 1);
582}
583
584void Menu::linkRight() {
585    if (iLink % linkColumns == linkColumns - 1
586            || iLink == (int)sectionLinks()->size() - 1)
587        setLinkIndex(iLink - iLink % linkColumns);
588    else
589        setLinkIndex(iLink + 1);
590}
591
592#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
593
594void Menu::linkUp() {
595    int l = iLink - linkColumns;
596    if (l < 0) {
597        const auto numLinks = sectionLinks()->size();
598        unsigned int rows = DIV_ROUND_UP(numLinks, linkColumns);
599        l = (rows * linkColumns) + l;
600        if (l >= static_cast<int>(numLinks))
601            l -= linkColumns;
602    }
603    setLinkIndex(l);
604}
605
606void Menu::linkDown() {
607    int l = iLink + linkColumns;
608    const auto numLinks = sectionLinks()->size();
609    if (l >= static_cast<int>(numLinks)) {
610        unsigned int rows = DIV_ROUND_UP(numLinks, linkColumns);
611        unsigned int curCol = DIV_ROUND_UP(iLink + 1, linkColumns);
612        if (rows > curCol)
613            l = numLinks - 1;
614        else
615            l %= linkColumns;
616    }
617    setLinkIndex(l);
618}
619
620int Menu::selLinkIndex() {
621    return iLink;
622}
623
624Link *Menu::selLink() {
625    if (sectionLinks()->size()==0) return NULL;
626    return sectionLinks()->at(iLink);
627}
628
629LinkApp *Menu::selLinkApp() {
630    return dynamic_cast<LinkApp*>(selLink());
631}
632
633void Menu::setLinkIndex(int i) {
634    const int numLinks = static_cast<int>(sectionLinks()->size());
635    if (i < 0)
636        i = numLinks - 1;
637    else if (i >= numLinks)
638        i = 0;
639    iLink = i;
640
641    int row = i / linkColumns;
642    if (row >= (int)(iFirstDispRow + linkRows - 1))
643        iFirstDispRow = min(row + 1, (int)DIV_ROUND_UP(numLinks, linkColumns) - 1)
644                        - linkRows + 1;
645    else if (row <= (int)iFirstDispRow)
646        iFirstDispRow = max(row - 1, 0);
647}
648
649#ifdef HAVE_LIBOPK
650void Menu::openPackagesFromDir(std::string path)
651{
652    if (access(path.c_str(), F_OK))
653        return;
654
655    DEBUG("Opening packages from directory: %s\n", path.c_str());
656    readPackages(path);
657#ifdef ENABLE_INOTIFY
658    monitors.push_back(new Monitor(path.c_str()));
659#endif
660}
661
662void Menu::openPackage(std::string path, bool order)
663{
664    /* First try to remove existing links of the same OPK
665     * (needed for instance when an OPK is modified) */
666    removePackageLink(path);
667
668    struct OPK *opk = opk_open(path.c_str());
669    if (!opk) {
670        ERROR("Unable to open OPK %s\n", path.c_str());
671        return;
672    }
673
674    for (;;) {
675        unsigned int i;
676        bool has_metadata = false;
677        LinkApp *link;
678
679        for (;;) {
680            string::size_type pos;
681            const char *name;
682            int ret = opk_open_metadata(opk, &name);
683            if (ret < 0) {
684                ERROR("Error while loading meta-data\n");
685                break;
686            } else if (!ret)
687              break;
688
689            /* Strip .desktop */
690            string metadata(name);
691            pos = metadata.rfind('.');
692            metadata = metadata.substr(0, pos);
693
694            /* Keep only the platform name */
695            pos = metadata.rfind('.');
696            metadata = metadata.substr(pos + 1);
697
698            if (!metadata.compare(PLATFORM) || !metadata.compare("all")) {
699                has_metadata = true;
700                break;
701            }
702        }
703
704        if (!has_metadata)
705          break;
706
707        link = new LinkApp(gmenu2x, path.c_str(), opk);
708        link->setSize(gmenu2x->skinConfInt["linkWidth"], gmenu2x->skinConfInt["linkHeight"]);
709
710        addSection(link->getCategory());
711        for (i = 0; i < sections.size(); i++) {
712            if (sections[i] == link->getCategory()) {
713                links[i].push_back(link);
714                break;
715            }
716        }
717    }
718
719    opk_close(opk);
720
721    if (order)
722        orderLinks();
723}
724
725void Menu::readPackages(std::string parentDir)
726{
727    DIR *dirp;
728    struct dirent *dptr;
729    vector<string> linkfiles;
730
731    dirp = opendir(parentDir.c_str());
732    if (!dirp)
733        return;
734
735    while ((dptr = readdir(dirp))) {
736        char *c;
737
738        if (dptr->d_type != DT_REG)
739            continue;
740
741        c = strrchr(dptr->d_name, '.');
742        if (!c) /* File without extension */
743            continue;
744
745        if (strcasecmp(c + 1, "opk"))
746            continue;
747
748        if (dptr->d_name[0] == '.') {
749            // Ignore hidden files.
750            // Mac OS X places these on SD cards, probably to store metadata.
751            continue;
752        }
753
754        openPackage(parentDir + '/' + dptr->d_name, false);
755    }
756
757    closedir(dirp);
758    orderLinks();
759}
760
761#ifdef ENABLE_INOTIFY
762/* Remove all links that correspond to the given path.
763 * If "path" is a directory, it will remove all links that
764 * correspond to an OPK present in the directory. */
765void Menu::removePackageLink(std::string path)
766{
767    for (vector< vector<Link*> >::iterator section = links.begin();
768                section < links.end(); section++) {
769        for (vector<Link*>::iterator link = section->begin();
770                    link < section->end(); link++) {
771            LinkApp *app = dynamic_cast<LinkApp *> (*link);
772            if (!app || !app->isOpk() || app->getOpkFile().empty())
773                continue;
774
775            if (app->getOpkFile().compare(0, path.size(), path) == 0) {
776                DEBUG("Removing link corresponding to package %s\n",
777                            app->getOpkFile().c_str());
778                section->erase(link);
779                if (section - links.begin() == iSection
780                            && iLink == (int) section->size())
781                    setLinkIndex(iLink - 1);
782                link--;
783            }
784        }
785    }
786
787    /* Remove registered monitors */
788    for (vector<Monitor *>::iterator it = monitors.begin();
789                it < monitors.end(); it++) {
790        if ((*it)->getPath().compare(0, path.size(), path) == 0) {
791            delete (*it);
792            monitors.erase(it);
793        }
794    }
795}
796#endif
797#endif
798
799void Menu::readLinksOfSection(std::string path, std::vector<std::string> &linkfiles)
800{
801    DIR *dirp;
802    struct dirent *dptr;
803
804    if ((dirp = opendir(path.c_str())) == NULL) return;
805
806    while ((dptr = readdir(dirp))) {
807        if (dptr->d_type != DT_REG) continue;
808        string filepath = path + "/" + dptr->d_name;
809        linkfiles.push_back(filepath);
810    }
811
812    closedir(dirp);
813}
814
815static bool compare_links(Link *a, Link *b)
816{
817    return a->getTitle().compare(b->getTitle()) <= 0;
818}
819
820void Menu::orderLinks()
821{
822    for (std::vector< std::vector<Link *> >::iterator section = links.begin();
823                section < links.end(); section++)
824        if (section->size() > 1)
825            std::sort(section->begin(), section->end(), compare_links);
826}
827
828void Menu::readLinks() {
829    vector<string> linkfiles;
830
831    iLink = 0;
832    iFirstDispRow = 0;
833    string filepath;
834
835    for (uint i=0; i<links.size(); i++) {
836        links[i].clear();
837        linkfiles.clear();
838
839        int correct = (i>sections.size() ? iSection : i);
840
841        readLinksOfSection(GMENU2X_SYSTEM_DIR "/sections/"
842          + sections[correct], linkfiles);
843
844        readLinksOfSection(GMenu2X::getHome() + "/sections/"
845          + sections[correct], linkfiles);
846
847        sort(linkfiles.begin(), linkfiles.end(),case_less());
848        for (uint x=0; x<linkfiles.size(); x++) {
849            LinkApp *link = new LinkApp(gmenu2x, linkfiles[x].c_str());
850            link->setSize(gmenu2x->skinConfInt["linkWidth"], gmenu2x->skinConfInt["linkHeight"]);
851            if (link->targetExists())
852                links[i].push_back(link);
853            else
854                delete link;
855        }
856    }
857}
858
859void Menu::renameSection(int index, const string &name) {
860    sections[index] = name;
861}
862

Archive Download this file



interactive