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

Archive Download this file



interactive