Root/src/menu.cpp

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

Archive Download this file



interactive