fixed a couple of bugs
[vrfileman] / src / fs.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <string>
6 #include <map>
7 #include <algorithm>
8 #include <unistd.h>
9 #include <dirent.h>
10 #include <sys/stat.h>
11 #include "fs.h"
12 #include "icon.h"
13 #include "gmath/gmath.h"
14 #include "opengl.h"
15 #include "app.h"
16 #include "drawtext.h"
17
18 static IconRenderer *iconrend;
19
20 static std::map<std::string, FSNode*> node_cache;
21 static FSNode *cur_node;
22 static int start_child;
23
24 static dtx_font *fat_font;
25 #define FAT_FONT_SZ     32
26
27
28 bool init_fs(const char *path)
29 {
30         iconrend = new ShapesIcons;
31         if(!iconrend->init()) {
32                 return false;
33         }
34
35         if(!(fat_font = dtx_open_font("data/fat.font", FAT_FONT_SZ))) {
36                 fprintf(stderr, "failed to open font file data/fat.font\n");
37                 return false;
38         }
39
40         if(!(cur_node = get_fsnode(path))) {
41                 return false;
42         }
43         cur_node->expand();
44         return true;
45 }
46
47 void cleanup_fs()
48 {
49         std::map<std::string, FSNode*>::iterator it = node_cache.begin();
50         while(it != node_cache.end()) {
51                 FSNode *node = it++->second;
52                 delete node;
53         }
54         node_cache.clear();
55         dtx_close_font(fat_font);
56         delete iconrend;
57 }
58
59 static Vec3 icon_pos(int row, int col, int ncols, float row_spacing, float radius)
60 {
61         float angle = 2.0 * M_PI * (float)col / (float)ncols;
62         float x = sin(angle) * radius;
63         float z = -cos(angle) * radius;
64         float y = (float)row * row_spacing;
65         return Vec3(x, y, z);
66 }
67
68 static float icon_angle(int col, int ncols, float max_angle = 0.0f)
69 {
70         if(max_angle > 0) {
71                 return max_angle * ((float)col / (float)(ncols - 1) - 0.5);
72         }
73         return 2.0 * M_PI * (float)col / (float)ncols;
74 }
75
76 void draw_fs()
77 {
78         static const float row_spacing = 0.25;
79         static const float radius = 0.6;
80         static const float umax = 0.42;
81         static const float max_icon_angle = M_PI * 2.0 * umax;
82
83         int max_ncols = std::max<int>(1, umax * 12);
84
85         Mat4 base_xform;
86         base_xform.rotate(time_sec, 0, 0);
87         base_xform.rotate(0, 0, time_sec * 0.5);
88         base_xform.translate(0, 1.65, 0);
89
90         glUseProgram(0);
91         glDisable(GL_TEXTURE_2D);
92
93         int nchildren = (int)cur_node->children.size();
94         int ncols = std::min(cur_node->nfiles, max_ncols);
95
96         int first = start_child % ncols;
97         int col = 0, row = 0;
98
99         for(int i=0; i<nchildren; i++) {
100                 int idx = (i + first) % nchildren;
101                 FSNode *node = cur_node->children[idx];
102
103                 if(node->type == FSTYPE_DIR) {
104                         continue;
105                 }
106
107                 float angle = icon_angle(col, ncols, max_icon_angle);
108
109                 Mat4 xform = base_xform;
110                 xform.translate(0, row * row_spacing, -radius);
111                 xform.rotate_y(angle);
112
113                 glPushMatrix();
114                 glMultMatrixf(xform[0]);
115                 iconrend->draw(node);
116                 glPopMatrix();
117
118                 glPushMatrix();
119                 xform = Mat4::identity;
120                 xform.translate(-dtx_string_width(node->path.get_name()) / 2.0, 0, 0);
121                 xform.scale(0.001);
122                 xform.translate(0, 1.54 + row * row_spacing, -radius);
123                 xform.rotate_y(angle);
124                 glMultMatrixf(xform[0]);
125
126                 dtx_string(node->path.get_name());
127                 glPopMatrix();
128
129                 if(++col >= ncols) {
130                         col = 0;
131                         ++row;
132                 }
133         }
134 }
135
136 FSNode *get_fsnode(const char *path)
137 {
138         char *abspath = make_abs_path(path);
139
140         FSNode *node = node_cache[abspath];
141         if(!node) {
142                 node = new FSNode;
143                 node->path = path;
144
145                 struct stat st;
146                 if(stat(node->path, &st) == -1) {
147                         fprintf(stderr, "failed to stat: %s\n", node->path.get_path());
148                         delete node;
149                         return 0;
150                 }
151                 node->size = st.st_size;
152
153                 switch(st.st_mode & S_IFMT) {
154                 case S_IFREG:
155                         node->type = FSTYPE_FILE;
156                         break;
157
158                 case S_IFDIR:
159                         node->type = FSTYPE_DIR;
160                         break;
161
162                 case S_IFBLK:
163                 case S_IFCHR:
164                         node->type = FSTYPE_DEV;
165                         break;
166
167                 default:
168                         node->type = FSTYPE_UNKNOWN;
169                 }
170                 node_cache[abspath] = node;
171         }
172
173         return node;
174 }
175
176 FSNode *get_fsnode(const char *dir, const char *name)
177 {
178         if(!dir) {
179                 return get_fsnode(name);
180         }
181         if(!name || *name == '/') {
182                 return 0;
183         }
184
185         int len = strlen(dir) + 1 + strlen(name);
186         char *buf = new char[len + 1];
187         sprintf(buf, "%s/%s", dir, name);
188         FSNode *res = get_fsnode(buf);
189         delete [] buf;
190         return res;
191 }
192
193 // ---- FSNode implementation ----
194 FSNode::FSNode()
195 {
196         type = FSTYPE_UNKNOWN;
197         size = 0;
198         parent = 0;
199         nfiles = ndirs = 0;
200 }
201
202 bool FSNode::expand()
203 {
204         if(type != FSTYPE_DIR) {
205                 return false;
206         }
207
208         DIR *dir = opendir(path);
209         if(!dir) {
210                 fprintf(stderr, "failed to open dir: %s: %s\n", path.get_path(), strerror(errno));
211                 return false;
212         }
213
214         struct dirent *dent;
215         while((dent = readdir(dir))) {
216                 if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
217                         continue;
218                 }
219
220                 FSNode *node = get_fsnode(path, dent->d_name);
221                 if(!node) continue;
222
223                 children.push_back(node);
224                 switch(node->type) {
225                 case FSTYPE_FILE:
226                         ++nfiles;
227                         break;
228                 case FSTYPE_DIR:
229                         ++ndirs;
230                 default:
231                         break;
232                 }
233         }
234         printf("expanded %d children\n", (int)children.size());
235
236         parent = get_fsnode(path.get_parent());
237         return true;
238 }