Commit | Line | Data |
---|---|---|
db4042d6 H |
1 | /* Copyright (C) 2008 Ricardo Catalinas Jiménez <jimenezrick@gmail.com> |
2 | * | |
3 | * This program is free software: you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License as published by | |
5 | * the Free Software Foundation, either version 3 of the License, or | |
6 | * (at your option) any later version. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include "fm.h" | |
18 | ||
19 | WINDOW *tree_window, *info_window; | |
20 | GList *lines, *selected_line, *first_line, *last_line; | |
21 | ||
22 | void init_lines(void) | |
23 | { | |
24 | lines = g_list_append(lines, tree_root); | |
25 | FILE(tree_root)->line = lines; | |
26 | selected_line = lines; | |
27 | first_line = lines; | |
28 | update_last_line(); | |
29 | } | |
30 | ||
31 | void init_curses(void) | |
32 | { | |
33 | int screen_size[2]; | |
34 | ||
35 | initscr(); | |
36 | #ifdef INOTIFY | |
37 | halfdelay(GETCH_DELAY); | |
38 | #else | |
39 | cbreak(); | |
40 | #endif | |
41 | nonl(); | |
42 | noecho(); | |
43 | keypad(stdscr, TRUE); | |
44 | curs_set(0); | |
45 | refresh(); | |
46 | ||
47 | getmaxyx(stdscr, screen_size[0], screen_size[1]); | |
48 | tree_window = newwin(screen_size[0] - 1, screen_size[1], 0, 0); | |
49 | info_window = newwin(1, screen_size[1], screen_size[0] - 1, 0); | |
50 | scrollok(tree_window, TRUE); | |
51 | idlok(tree_window, TRUE); | |
52 | } | |
53 | ||
54 | void init_ui(char *name) | |
55 | { | |
56 | init_curses(); | |
57 | init_tree(name); | |
58 | init_lines(); | |
59 | print_line(FILE(tree_root)->line); | |
60 | #ifdef INOTIFY | |
61 | init_inotify(tree_root); | |
62 | #endif | |
63 | open_directory(tree_root); | |
64 | refresh_screen(); | |
65 | } | |
66 | ||
67 | void refresh_screen(void) | |
68 | { | |
69 | wnoutrefresh(tree_window); | |
70 | wnoutrefresh(info_window); | |
71 | doupdate(); | |
72 | } | |
73 | ||
74 | void handle_sigwinch(int signal_number) | |
75 | { | |
76 | ungetch(KEY_RESIZE); | |
77 | } | |
78 | ||
79 | void print_lines(GList *start_line, GList *end_line, gboolean clear_bottom_lines) | |
80 | { | |
81 | GList *line_ptr; | |
82 | int line_number; | |
83 | ||
84 | if (end_line == NULL) | |
85 | end_line = last_line; | |
86 | else if (start_line != NULL) { | |
87 | for (line_ptr = start_line; | |
88 | g_list_position(first_line, line_ptr) < getmaxy(tree_window); | |
89 | line_ptr = g_list_next(line_ptr)) { | |
90 | print_line(line_ptr); | |
91 | if (line_ptr == end_line) | |
92 | break; | |
93 | } | |
94 | } | |
95 | line_number = g_list_position(first_line, last_line) + 1; | |
96 | if (clear_bottom_lines && line_number < getmaxy(tree_window)) { | |
97 | wmove(tree_window, line_number, 0); | |
98 | wclrtobot(tree_window); | |
99 | } | |
100 | } | |
101 | ||
102 | void print_parents_lines(GList *line) | |
103 | { | |
104 | GNode *file_ptr; | |
105 | int depth; | |
106 | ||
107 | for (file_ptr = NODE(line)->parent, depth = g_node_depth(file_ptr) - 1; | |
108 | !G_NODE_IS_ROOT(file_ptr); file_ptr = file_ptr->parent, depth--) { | |
109 | if (file_ptr != g_node_last_sibling(file_ptr)) | |
110 | mvwaddch(tree_window, g_list_position(first_line, line), | |
111 | 2 * depth - 2, ACS_VLINE); | |
112 | else | |
113 | mvwaddch(tree_window, g_list_position(first_line, line), | |
114 | 2 * depth - 2, ' '); | |
115 | mvwaddch(tree_window, g_list_position(first_line, line), 2 * depth - 1, ' '); | |
116 | } | |
117 | } | |
118 | ||
119 | void print_line(GList *line) | |
120 | { | |
121 | int line_number = g_list_position(first_line, line); | |
122 | GNode *file = NODE(line); | |
123 | int depth = g_node_depth(file) - 1; | |
124 | char *link_str; | |
125 | ||
126 | wmove(tree_window, line_number, 0); | |
127 | wclrtoeol(tree_window); | |
128 | ||
129 | if (line == selected_line) | |
130 | wattron(tree_window, A_REVERSE); | |
131 | ||
132 | if (G_NODE_IS_ROOT(file)) { | |
133 | wattron(tree_window, A_BOLD); | |
134 | waddnstr(tree_window, FILE(file)->name, getmaxx(tree_window) - 1); | |
135 | if (!g_str_has_suffix(FILE(file)->name, "/")) | |
136 | waddch(tree_window, '/'); | |
137 | wattroff(tree_window, A_BOLD); | |
138 | } else { | |
139 | if (file != g_node_last_sibling(file)) | |
140 | mvwaddch(tree_window, line_number, 2 * depth - 2, ACS_LTEE); | |
141 | else | |
142 | mvwaddch(tree_window, line_number, 2 * depth - 2, ACS_LLCORNER); | |
143 | ||
144 | waddch(tree_window, ACS_HLINE); | |
145 | if (FILE(file)->link == TRUE) { | |
146 | link_str = g_strdup_printf("%s -> %s", FILE(file)->name, | |
147 | FILE(file)->link_path); | |
148 | waddnstr(tree_window, link_str, getmaxx(tree_window) - 2 * depth - 1); | |
149 | free(link_str); | |
150 | } else | |
151 | waddnstr(tree_window, FILE(file)->name, | |
152 | getmaxx(tree_window) - 2 * depth - 1); | |
153 | ||
154 | if (FILE(file)->type == directory_type && ((FILE(file)->link == TRUE && | |
155 | !g_str_has_suffix(FILE(file)->link_path, "/")) || | |
156 | (FILE(file)->link == FALSE && | |
157 | !g_str_has_suffix(FILE(file)->name, "/")))) | |
158 | waddch(tree_window, '/'); | |
159 | print_parents_lines(line); | |
160 | } | |
161 | wattroff(tree_window, A_REVERSE); | |
162 | } | |
163 | ||
164 | void clear_info(void) | |
165 | { | |
166 | werase(info_window); | |
167 | } | |
168 | ||
169 | void print_info(char *message, gboolean bold) | |
170 | { | |
171 | static char *last_message = NULL; | |
172 | static gboolean last_bold = FALSE; | |
173 | ||
174 | if (message != NULL) { | |
175 | if (last_message != NULL) | |
176 | free(last_message); | |
177 | last_message = strdup(message); | |
178 | } | |
179 | if (bold == TRUE || bold == FALSE) | |
180 | last_bold = bold; | |
181 | if (last_bold == TRUE) | |
182 | wattron(info_window, A_BOLD); | |
183 | werase(info_window); | |
184 | if (last_message != NULL) | |
185 | waddnstr(info_window, last_message, getmaxx(info_window)); | |
186 | wattroff(info_window, A_BOLD); | |
187 | } | |
188 | ||
189 | void print_error_info(int last_errno, char *file, int line, char *message) | |
190 | { | |
191 | char *message_format = "Error (%s:%i): %s"; | |
192 | char *message_str; | |
193 | ||
194 | if (message != NULL) | |
195 | message_str = g_strdup_printf(message_format, file, line, message); | |
196 | else | |
197 | message_str = g_strdup_printf(message_format, file, line, strerror(last_errno)); | |
198 | print_info(message_str, TRUE); | |
199 | free(message_str); | |
200 | } | |
201 | ||
202 | void update_last_line(void) | |
203 | { | |
204 | if (g_list_length(first_line) > getmaxy(tree_window)) | |
205 | last_line = g_list_nth(first_line, getmaxy(tree_window) - 1); | |
206 | else | |
207 | last_line = g_list_last(lines); | |
208 | } | |
209 | ||
210 | void add_directory_content(GNode *directory) | |
211 | { | |
212 | GNode *file_ptr, *directory_sibling; | |
213 | int position; | |
214 | ||
215 | directory_sibling = get_next_file_not_deepper(directory); | |
216 | ||
217 | for (position = g_list_position(lines, FILE(directory)->line) + 1, | |
218 | file_ptr = g_node_first_child(directory); | |
219 | file_ptr != NULL && file_ptr != directory_sibling; | |
220 | position++, file_ptr = get_next_file(file_ptr)) { | |
221 | lines = g_list_insert(lines, file_ptr, position); | |
222 | FILE(file_ptr)->line = g_list_nth(lines, position); | |
223 | } | |
224 | } | |
225 | ||
226 | void open_directory(GNode *directory) | |
227 | { | |
228 | if (FILE(directory)->read == FALSE && read_directory(directory) == FALSE) | |
229 | return; | |
230 | ||
231 | FILE(directory)->open = TRUE; | |
232 | add_directory_content(directory); | |
233 | update_last_line(); | |
234 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
235 | } | |
236 | ||
237 | void close_directory(GNode *directory) | |
238 | { | |
239 | GNode *directory_sibling; | |
240 | GList *line_ptr; | |
241 | ||
242 | directory_sibling = get_next_file_not_deepper(directory); | |
243 | ||
244 | for (line_ptr = g_list_next(FILE(directory)->line); line_ptr != NULL && | |
245 | NODE(line_ptr) != directory_sibling; line_ptr = g_list_next(FILE(directory)->line)) | |
246 | lines = g_list_delete_link(lines, line_ptr); | |
247 | ||
248 | FILE(directory)->open = FALSE; | |
249 | update_last_line(); | |
250 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
251 | } | |
252 | ||
253 | void update_directory(GNode *directory) | |
254 | { | |
255 | close_directory(directory); | |
256 | destroy_directory_content(directory); | |
257 | open_directory(directory); | |
258 | ||
259 | if (FILE(directory)->show_dotfiles == TRUE) | |
260 | show_dotfiles(directory); | |
261 | } | |
262 | ||
263 | void show_dotfiles(GNode *directory) | |
264 | { | |
265 | add_dotfiles(directory); | |
266 | update_last_line(); | |
267 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
268 | } | |
269 | ||
270 | void hide_dotfiles(GNode *directory) | |
271 | { | |
272 | remove_dotfiles(directory); | |
273 | update_last_line(); | |
274 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
275 | } | |
276 | ||
277 | void select_line(GList *line) | |
278 | { | |
279 | select_nth_line(g_list_position(lines, line) - g_list_position(lines, selected_line)); | |
280 | } | |
281 | ||
282 | void select_file(GNode *file) | |
283 | { | |
284 | select_line(FILE(file)->line); | |
285 | } | |
286 | ||
287 | void select_nth_line(int n_lines) | |
288 | { | |
289 | GList *old_selected_line; | |
290 | int min_n_lines, max_n_lines; | |
291 | ||
292 | if ((max_n_lines = g_list_length(selected_line) - 1) < n_lines) | |
293 | n_lines = max_n_lines; | |
294 | else if((min_n_lines = (int) g_list_length(selected_line) - | |
295 | (int) g_list_length(lines)) > n_lines) | |
296 | n_lines = min_n_lines; | |
297 | ||
298 | old_selected_line = selected_line; | |
299 | if (n_lines < 0) | |
300 | selected_line = g_list_nth(lines, g_list_position(lines, selected_line) + n_lines); | |
301 | else if (n_lines > 0) | |
302 | selected_line = g_list_nth(selected_line, n_lines); | |
303 | ||
304 | if (g_list_position(lines, selected_line) < g_list_position(lines, first_line)) | |
305 | scroll_tree(g_list_position(lines, selected_line) - | |
306 | g_list_position(lines, first_line)); | |
307 | else if (g_list_position(lines, selected_line) > g_list_position(lines, last_line)) | |
308 | scroll_tree(g_list_position(lines, selected_line) - | |
309 | g_list_position(lines, last_line)); | |
310 | ||
311 | if (n_lines != 0) { | |
312 | print_line(selected_line); | |
313 | if (g_list_position(lines, first_line) <= g_list_position(lines, old_selected_line) && | |
314 | g_list_position(lines, old_selected_line) <= | |
315 | g_list_position(lines, last_line)) | |
316 | print_line(old_selected_line); | |
317 | } | |
318 | } | |
319 | ||
320 | void scroll_tree(int n_lines) | |
321 | { | |
322 | int min_n_lines, max_n_lines; | |
323 | ||
324 | if ((min_n_lines = -g_list_position(lines, first_line)) > n_lines) | |
325 | n_lines = min_n_lines; | |
326 | else if ((max_n_lines = g_list_length(last_line) - 1) < n_lines) | |
327 | n_lines = max_n_lines; | |
328 | ||
329 | wscrl(tree_window, n_lines); | |
330 | if (n_lines < 0) { | |
331 | first_line = g_list_nth(lines, g_list_position(lines, first_line) + n_lines); | |
332 | update_last_line(); | |
333 | print_lines(first_line, g_list_nth(first_line, -n_lines), FALSE); | |
334 | } else if (n_lines > 0) { | |
335 | first_line = g_list_nth(first_line, n_lines); | |
336 | update_last_line(); | |
337 | print_lines(g_list_nth(lines, g_list_position(lines, last_line) - n_lines), | |
338 | last_line, TRUE); | |
339 | } | |
340 | } | |
341 | ||
342 | void select_line_inside_window(void) | |
343 | { | |
344 | if (g_list_position(lines, selected_line) < g_list_position(lines, first_line)) | |
345 | select_line(first_line); | |
346 | else if (g_list_position(lines, selected_line) > g_list_position(lines, last_line)) | |
347 | select_line(last_line); | |
348 | } | |
349 | ||
350 | void select_next_line_by_first_letter(char letter) | |
351 | { | |
352 | GList *line_ptr; | |
353 | ||
354 | for (line_ptr = g_list_next(selected_line); line_ptr != NULL; | |
355 | line_ptr = g_list_next(line_ptr)) { | |
356 | if (FILE(NODE(line_ptr))->name[0] == letter) { | |
357 | select_line(line_ptr); | |
358 | return; | |
359 | } | |
360 | } | |
361 | } | |
362 | ||
363 | void select_previous_line_by_first_letter(char letter) | |
364 | { | |
365 | GList *line_ptr; | |
366 | ||
367 | for (line_ptr = g_list_previous(selected_line); line_ptr != NULL; | |
368 | line_ptr = g_list_previous(line_ptr)) { | |
369 | if (FILE(NODE(line_ptr))->name[0] == letter) { | |
370 | select_line(line_ptr); | |
371 | return; | |
372 | } | |
373 | } | |
374 | } |