Root/src/menu.cpp

Source at commit 73ceffa51d3450d2d20ce933e7fc6da187e09fc8 created 7 years 2 months ago.
By Maarten ter Huurne, Fix bug in section directory creation method
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        DIR *dirp = opendir(CARD_ROOT);
85        if (dirp) {
86            struct dirent *dptr;
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 = opendir(parentDir.c_str());
112    if (!dirp) return;
113
114    struct dirent *dptr;
115    while ((dptr = readdir(dirp))) {
116        if (dptr->d_name[0] != '.' && dptr->d_type == DT_DIR) {
117            // Create section if it doesn't exist yet.
118            sectionNamed(dptr->d_name);
119        }
120    }
121
122    closedir(dirp);
123}
124
125string Menu::createSectionDir(string const& sectionName)
126{
127    string parentDir = GMenu2X::getHome() + "/sections";
128    if (mkdir(parentDir.c_str(), 0755) && errno != EEXIST) {
129        WARNING("Failed to create parent sections dir: %s\n", strerror(errno));
130        return "";
131    }
132
133    string childDir = parentDir + "/" + sectionName;
134    if (mkdir(childDir.c_str(), 0755) && errno != EEXIST) {
135        WARNING("Failed to create child section dir: %s\n", strerror(errno));
136        return "";
137    }
138
139    return childDir;
140}
141
142void Menu::skinUpdated() {
143    ConfIntHash &skinConfInt = gmenu2x.skinConfInt;
144
145    //recalculate some coordinates based on the new element sizes
146    linkColumns = (gmenu2x.resX - 10) / skinConfInt["linkWidth"];
147    linkRows = (gmenu2x.resY - 35 - skinConfInt["topBarHeight"]) / skinConfInt["linkHeight"];
148
149    //reload section icons
150    decltype(links)::size_type i = 0;
151    for (auto& sectionName : sections) {
152        gmenu2x.sc["skin:sections/" + sectionName + ".png"];
153
154        for (auto& 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.getLineSpacing()) / 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        font.write(s, 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    auto& sectionLinks = links[iSection];
233    auto 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        font.write(s, 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        font.write(s, 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
277bool Menu::handleButtonPress(InputManager::Button button) {
278    switch (button) {
279        case InputManager::ACCEPT:
280            if (selLink() != NULL) selLink()->run();
281            return true;
282        case InputManager::UP:
283            linkUp();
284            return true;
285        case InputManager::DOWN:
286            linkDown();
287            return true;
288        case InputManager::LEFT:
289            linkLeft();
290            return true;
291        case InputManager::RIGHT:
292            linkRight();
293            return true;
294        case InputManager::ALTLEFT:
295            decSectionIndex();
296            return true;
297        case InputManager::ALTRIGHT:
298            incSectionIndex();
299            return true;
300        case InputManager::MENU:
301            gmenu2x.showContextMenu();
302            return true;
303        default:
304            return false;
305    }
306}
307
308/*====================================
309   SECTION MANAGEMENT
310  ====================================*/
311
312vector<unique_ptr<Link>> *Menu::sectionLinks(int i)
313{
314    if (i<0 || i>=(int)links.size()) {
315        i = selSectionIndex();
316    }
317
318    if (i<0 || i>=(int)links.size()) {
319        return nullptr;
320    }
321
322    return &links[i];
323}
324
325void Menu::decSectionIndex() {
326    sectionAnimation.adjust(-1 << 16);
327    setSectionIndex(iSection - 1);
328}
329
330void Menu::incSectionIndex() {
331    sectionAnimation.adjust(1 << 16);
332    setSectionIndex(iSection + 1);
333}
334
335int Menu::selSectionIndex() {
336    return iSection;
337}
338
339const string &Menu::selSection() {
340    return sections[iSection];
341}
342
343void Menu::setSectionIndex(int i) {
344    if (i<0)
345        i=sections.size()-1;
346    else if (i>=(int)sections.size())
347        i=0;
348    iSection = i;
349
350    iLink = 0;
351    iFirstDispRow = 0;
352}
353
354/*====================================
355   LINKS MANAGEMENT
356  ====================================*/
357void Menu::addActionLink(uint section, string const& title, Action action,
358        string const& description, string const& icon)
359{
360    assert(section < sections.size());
361
362    Link *link = new Link(gmenu2x, action);
363    link->setSize(gmenu2x.skinConfInt["linkWidth"], gmenu2x.skinConfInt["linkHeight"]);
364    link->setTitle(title);
365    link->setDescription(description);
366    if (gmenu2x.sc.exists(icon)
367            || (icon.substr(0,5)=="skin:"
368                && !gmenu2x.sc.getSkinFilePath(icon.substr(5,icon.length())).empty())
369            || fileExists(icon)) {
370        link->setIcon(icon);
371    }
372
373    links[section].emplace_back(link);
374}
375
376bool Menu::addLink(string const& path, string const& file)
377{
378    string const& sectionName = selSection();
379
380    string sectionDir = createSectionDir(sectionName);
381    if (sectionDir.empty()) {
382        return false;
383    }
384
385    //strip the extension from the filename
386    string title = file;
387    string::size_type pos = title.rfind(".");
388    if (pos!=string::npos && pos>0) {
389        string ext = title.substr(pos, title.length());
390        transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
391        title = title.substr(0, pos);
392    }
393
394    string linkpath = sectionDir + "/" + title;
395    int x = 2;
396    while (fileExists(linkpath)) {
397        stringstream ss;
398        ss << sectionDir << '/' << title << x;
399        ss >> linkpath;
400        x++;
401    }
402    INFO("Adding link: '%s'\n", linkpath.c_str());
403
404    string dirPath = path;
405    if (dirPath.empty() || dirPath.back() != '/') dirPath += '/';
406
407    //search for a manual
408    pos = file.rfind(".");
409    string exename = dirPath + file.substr(0, pos);
410    string manual = "";
411    if (fileExists(exename+".man.png")) {
412        manual = exename+".man.png";
413    } else if (fileExists(exename+".man.txt")) {
414        manual = exename+".man.txt";
415    } else {
416        //scan directory for a file like *readme*
417        FileLister fl;
418        fl.setShowDirectories(false);
419        fl.setFilter(".txt");
420        fl.browse(dirPath);
421        bool found = false;
422        for (uint x=0; x<fl.size() && !found; x++) {
423            string lcfilename = fl[x];
424
425            if (lcfilename.find("readme") != string::npos) {
426                found = true;
427                manual = dirPath + fl.getFiles()[x];
428            }
429        }
430    }
431
432    INFO("Manual: '%s'\n", manual.c_str());
433
434    string shorttitle=title, description="", exec=dirPath+file, icon="";
435    if (fileExists(exename+".png")) icon = exename+".png";
436
437    //Reduce title lenght to fit the link width
438    if (gmenu2x.font->getTextWidth(shorttitle)>gmenu2x.skinConfInt["linkWidth"]) {
439        while (gmenu2x.font->getTextWidth(shorttitle+"..")>gmenu2x.skinConfInt["linkWidth"])
440            shorttitle = shorttitle.substr(0,shorttitle.length()-1);
441        shorttitle += "..";
442    }
443
444    ofstream f(linkpath.c_str());
445    if (f.is_open()) {
446        f << "title=" << shorttitle << endl;
447        f << "exec=" << exec << endl;
448        if (!description.empty()) f << "description=" << description << endl;
449        if (!icon.empty()) f << "icon=" << icon << endl;
450        if (!manual.empty()) f << "manual=" << manual << endl;
451        f.close();
452         sync();
453
454        auto idx = sectionNamed(sectionName);
455        auto link = new LinkApp(gmenu2x, linkpath, true);
456        link->setSize(gmenu2x.skinConfInt["linkWidth"], gmenu2x.skinConfInt["linkHeight"]);
457        links[idx].emplace_back(link);
458    } else {
459
460        ERROR("Error while opening the file '%s' for write.\n", linkpath.c_str());
461
462        return false;
463    }
464
465    return true;
466}
467
468int Menu::sectionNamed(const char *sectionName)
469{
470    auto it = lower_bound(sections.begin(), sections.end(), sectionName);
471    int idx = it - sections.begin();
472    if (it == sections.end() || *it != sectionName) {
473        sections.emplace(it, sectionName);
474        links.emplace(links.begin() + idx);
475        // Make sure the selected section doesn't change.
476        if (idx <= iSection) {
477            iSection++;
478        }
479    }
480    return idx;
481}
482
483void Menu::deleteSelectedLink()
484{
485    string iconpath = selLink()->getIconPath();
486
487    INFO("Deleting link '%s'\n", selLink()->getTitle().c_str());
488
489    if (selLinkApp()!=NULL)
490        unlink(selLinkApp()->getFile().c_str());
491    sectionLinks()->erase( sectionLinks()->begin() + selLinkIndex() );
492    setLinkIndex(selLinkIndex());
493
494    bool icon_used = false;
495    for (auto& section : links) {
496        for (auto& link : section) {
497            if (iconpath == link->getIconPath()) {
498                icon_used = true;
499            }
500        }
501    }
502    if (!icon_used) {
503        gmenu2x.sc.del(iconpath);
504    }
505}
506
507void Menu::deleteSelectedSection()
508{
509    string const& sectionName = selSection();
510    INFO("Deleting section '%s'\n", sectionName.c_str());
511
512    gmenu2x.sc.del("sections/" + sectionName + ".png");
513    auto idx = selSectionIndex();
514    links.erase(links.begin() + idx);
515    sections.erase(sections.begin() + idx);
516    setSectionIndex(0); //reload sections
517
518    string path = GMenu2X::getHome() + "/sections/" + sectionName;
519    if (rmdir(path.c_str()) && errno != ENOENT) {
520        WARNING("Removal of section dir \"%s\" failed: %s\n",
521                path.c_str(), strerror(errno));
522    }
523}
524
525bool Menu::linkChangeSection(uint linkIndex, uint oldSectionIndex, uint newSectionIndex) {
526    // Fetch sections.
527    auto oldSectionLinks = sectionLinks(oldSectionIndex);
528    if (!oldSectionLinks) return false;
529    auto newSectionLinks = sectionLinks(newSectionIndex);
530    if (!newSectionLinks) return false;
531
532    // Find link in old section.
533    if (linkIndex >= oldSectionLinks->size()) return false;
534    auto it = oldSectionLinks->begin() + linkIndex;
535
536    // Move link.
537    auto link = it->release();
538    oldSectionLinks->erase(it);
539    newSectionLinks->emplace_back(link);
540
541    // Select the same link in the new section.
542    setSectionIndex(newSectionIndex);
543    setLinkIndex(newSectionLinks->size() - 1);
544
545    return true;
546}
547
548void Menu::linkLeft() {
549    if (iLink % linkColumns == 0)
550        setLinkIndex(sectionLinks()->size() > iLink + linkColumns - 1
551                ? iLink + linkColumns - 1 : sectionLinks()->size() - 1);
552    else
553        setLinkIndex(iLink - 1);
554}
555
556void Menu::linkRight() {
557    if (iLink % linkColumns == linkColumns - 1
558            || iLink == (int)sectionLinks()->size() - 1)
559        setLinkIndex(iLink - iLink % linkColumns);
560    else
561        setLinkIndex(iLink + 1);
562}
563
564#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
565
566void Menu::linkUp() {
567    int l = iLink - linkColumns;
568    if (l < 0) {
569        const auto numLinks = sectionLinks()->size();
570        unsigned int rows = DIV_ROUND_UP(numLinks, linkColumns);
571        l = (rows * linkColumns) + l;
572        if (l >= static_cast<int>(numLinks))
573            l -= linkColumns;
574    }
575    setLinkIndex(l);
576}
577
578void Menu::linkDown() {
579    int l = iLink + linkColumns;
580    const auto numLinks = sectionLinks()->size();
581    if (l >= static_cast<int>(numLinks)) {
582        unsigned int rows = DIV_ROUND_UP(numLinks, linkColumns);
583        unsigned int curCol = DIV_ROUND_UP(iLink + 1, linkColumns);
584        if (rows > curCol)
585            l = numLinks - 1;
586        else
587            l %= linkColumns;
588    }
589    setLinkIndex(l);
590}
591
592int Menu::selLinkIndex() {
593    return iLink;
594}
595
596Link *Menu::selLink() {
597    auto curSectionLinks = sectionLinks();
598    if (curSectionLinks->empty()) {
599        return nullptr;
600    } else {
601        return curSectionLinks->at(iLink).get();
602    }
603}
604
605LinkApp *Menu::selLinkApp() {
606    return dynamic_cast<LinkApp*>(selLink());
607}
608
609void Menu::setLinkIndex(int i) {
610    auto links = sectionLinks();
611    if (!links) {
612        // No sections.
613        return;
614    }
615    const int numLinks = static_cast<int>(sectionLinks()->size());
616    if (i < 0)
617        i = numLinks - 1;
618    else if (i >= numLinks)
619        i = 0;
620    iLink = i;
621
622    int row = i / linkColumns;
623    if (row >= (int)(iFirstDispRow + linkRows - 1))
624        iFirstDispRow = min(row + 1, (int)DIV_ROUND_UP(numLinks, linkColumns) - 1)
625                        - linkRows + 1;
626    else if (row <= (int)iFirstDispRow)
627        iFirstDispRow = max(row - 1, 0);
628}
629
630#ifdef HAVE_LIBOPK
631void Menu::openPackagesFromDir(std::string const& path)
632{
633    DEBUG("Opening packages from directory: %s\n", path.c_str());
634    if (readPackages(path)) {
635#ifdef ENABLE_INOTIFY
636        monitors.emplace_back(new Monitor(path.c_str()));
637#endif
638    }
639}
640
641void Menu::openPackage(std::string const& path, bool order)
642{
643    /* First try to remove existing links of the same OPK
644     * (needed for instance when an OPK is modified) */
645    removePackageLink(path);
646
647    struct OPK *opk = opk_open(path.c_str());
648    if (!opk) {
649        ERROR("Unable to open OPK %s\n", path.c_str());
650        return;
651    }
652
653    for (;;) {
654        bool has_metadata = false;
655        const char *name;
656
657        for (;;) {
658            string::size_type pos;
659            int ret = opk_open_metadata(opk, &name);
660            if (ret < 0) {
661                ERROR("Error while loading meta-data\n");
662                break;
663            } else if (!ret)
664              break;
665
666            /* Strip .desktop */
667            string metadata(name);
668            pos = metadata.rfind('.');
669            metadata = metadata.substr(0, pos);
670
671            /* Keep only the platform name */
672            pos = metadata.rfind('.');
673            metadata = metadata.substr(pos + 1);
674
675            if (metadata == PLATFORM || metadata == "all") {
676                has_metadata = true;
677                break;
678            }
679        }
680
681        if (!has_metadata)
682          break;
683
684        // Note: OPK links can only be deleted by removing the OPK itself,
685        // but that is not something we want to do in the menu,
686        // so consider this link undeletable.
687        auto link = new LinkApp(gmenu2x, path, false, opk, name);
688        link->setSize(gmenu2x.skinConfInt["linkWidth"], gmenu2x.skinConfInt["linkHeight"]);
689
690        auto idx = sectionNamed(link->getCategory());
691        links[idx].emplace_back(link);
692    }
693
694    opk_close(opk);
695
696    if (order)
697        orderLinks();
698}
699
700bool Menu::readPackages(std::string const& parentDir)
701{
702    DIR *dirp = opendir(parentDir.c_str());
703    if (!dirp) {
704        return false;
705    }
706
707    while (struct dirent *dptr = readdir(dirp)) {
708        if (dptr->d_type != DT_REG)
709            continue;
710
711        char *c = strrchr(dptr->d_name, '.');
712        if (!c) /* File without extension */
713            continue;
714
715        if (strcasecmp(c + 1, "opk"))
716            continue;
717
718        if (dptr->d_name[0] == '.') {
719            // Ignore hidden files.
720            // Mac OS X places these on SD cards, probably to store metadata.
721            continue;
722        }
723
724        openPackage(parentDir + '/' + dptr->d_name, false);
725    }
726
727    closedir(dirp);
728    orderLinks();
729
730    return true;
731}
732
733#ifdef ENABLE_INOTIFY
734/* Remove all links that correspond to the given path.
735 * If "path" is a directory, it will remove all links that
736 * correspond to an OPK present in the directory. */
737void Menu::removePackageLink(std::string const& path)
738{
739    for (auto section = links.begin(); section != links.end(); ++section) {
740        for (auto link = section->begin(); link != section->end(); ++link) {
741            LinkApp *app = dynamic_cast<LinkApp *>(link->get());
742            if (!app || !app->isOpk() || app->getOpkFile().empty())
743                continue;
744
745            if (app->getOpkFile().compare(0, path.size(), path) == 0) {
746                DEBUG("Removing link corresponding to package %s\n",
747                            app->getOpkFile().c_str());
748                section->erase(link);
749                if (section - links.begin() == iSection
750                            && iLink == (int) section->size()) {
751                    setLinkIndex(iLink - 1);
752                }
753                --link;
754            }
755        }
756    }
757
758    /* Remove registered monitors */
759    for (auto it = monitors.begin(); it < monitors.end(); ++it) {
760        if ((*it)->getPath().compare(0, path.size(), path) == 0) {
761            monitors.erase(it);
762        }
763    }
764}
765#endif
766#endif
767
768static bool compare_links(unique_ptr<Link> const& a, unique_ptr<Link> const& b)
769{
770    LinkApp *app1 = dynamic_cast<LinkApp *>(a.get());
771    LinkApp *app2 = dynamic_cast<LinkApp *>(b.get());
772    bool app1_is_opk = app1 && app1->isOpk(),
773         app2_is_opk = app2 && app2->isOpk();
774
775    if (app1_is_opk && !app2_is_opk) {
776        return false;
777    }
778    if (app2_is_opk && !app1_is_opk) {
779        return true;
780    }
781    return a->getTitle().compare(b->getTitle()) < 0;
782}
783
784void Menu::orderLinks()
785{
786    for (auto& section : links) {
787        sort(section.begin(), section.end(), compare_links);
788    }
789}
790
791void Menu::readLinks()
792{
793    iLink = 0;
794    iFirstDispRow = 0;
795
796    for (uint i=0; i<links.size(); i++) {
797        links[i].clear();
798
799        int correct = (i>sections.size() ? iSection : i);
800        string const& section = sections[correct];
801
802        readLinksOfSection(
803                links[i], GMENU2X_SYSTEM_DIR "/sections/" + section, false);
804        readLinksOfSection(
805                links[i], GMenu2X::getHome() + "/sections/" + section, true);
806    }
807
808    orderLinks();
809}
810
811void Menu::readLinksOfSection(
812        vector<unique_ptr<Link>>& links, string const& path, bool deletable)
813{
814    DIR *dirp = opendir(path.c_str());
815    if (!dirp) return;
816
817    while (struct dirent *dptr = readdir(dirp)) {
818        if (dptr->d_type != DT_REG) continue;
819        string linkfile = path + '/' + dptr->d_name;
820
821        LinkApp *link = new LinkApp(gmenu2x, linkfile, deletable);
822        if (link->targetExists()) {
823            link->setSize(
824                    gmenu2x.skinConfInt["linkWidth"],
825                    gmenu2x.skinConfInt["linkHeight"]);
826            links.emplace_back(link);
827        } else {
828            delete link;
829        }
830    }
831
832    closedir(dirp);
833}
834

Archive Download this file



interactive