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