Commit | Line | Data |
---|---|---|
21c4e167 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 | cbreak(); | |
37 | nonl(); | |
38 | noecho(); | |
39 | keypad(stdscr, TRUE); | |
40 | curs_set(0); | |
41 | refresh(); | |
42 | ||
43 | getmaxyx(stdscr, screen_size[0], screen_size[1]); | |
44 | tree_window = newwin(screen_size[0] - 1, screen_size[1], 0, 0); | |
45 | info_window = newwin(1, screen_size[1], screen_size[0] - 1, 0); | |
46 | scrollok(tree_window, TRUE); | |
47 | idlok(tree_window, TRUE); | |
48 | } | |
49 | ||
50 | void init_ui(char *name) | |
51 | { | |
52 | init_curses(); | |
53 | init_tree(name); | |
54 | init_lines(); | |
55 | print_line(FILE(tree_root)->line); | |
56 | open_directory(tree_root); | |
57 | refresh_screen(); | |
58 | } | |
59 | ||
60 | void refresh_screen(void) | |
61 | { | |
62 | wnoutrefresh(tree_window); | |
63 | wnoutrefresh(info_window); | |
64 | doupdate(); | |
65 | } | |
66 | ||
67 | void handle_sigwinch(int signal_number) | |
68 | { | |
69 | ungetch(KEY_RESIZE); | |
70 | } | |
71 | ||
72 | void print_lines(GList *start_line, GList *end_line, gboolean clear_bottom_lines) | |
73 | { | |
74 | GList *line_ptr; | |
75 | int line_number; | |
76 | ||
77 | if (end_line == NULL) | |
78 | end_line = last_line; | |
79 | else if (start_line != NULL) { | |
80 | for (line_ptr = start_line; | |
81 | g_list_position(first_line, line_ptr) < getmaxy(tree_window); | |
82 | line_ptr = g_list_next(line_ptr)) { | |
83 | print_line(line_ptr); | |
84 | if (line_ptr == end_line) | |
85 | break; | |
86 | } | |
87 | } | |
88 | line_number = g_list_position(first_line, last_line) + 1; | |
89 | if (clear_bottom_lines && line_number < getmaxy(tree_window)) { | |
90 | wmove(tree_window, line_number, 0); | |
91 | wclrtobot(tree_window); | |
92 | } | |
93 | } | |
94 | ||
95 | void print_parents_lines(GList *line) | |
96 | { | |
97 | GNode *file_ptr; | |
98 | int depth; | |
99 | ||
100 | for (file_ptr = NODE(line)->parent, depth = g_node_depth(file_ptr) - 1; | |
101 | !G_NODE_IS_ROOT(file_ptr); file_ptr = file_ptr->parent, depth--) { | |
102 | if (file_ptr != g_node_last_sibling(file_ptr)) | |
103 | mvwaddch(tree_window, g_list_position(first_line, line), | |
104 | 2 * depth - 2, ACS_VLINE); | |
105 | else | |
106 | mvwaddch(tree_window, g_list_position(first_line, line), | |
107 | 2 * depth - 2, ' '); | |
108 | mvwaddch(tree_window, g_list_position(first_line, line), 2 * depth - 1, ' '); | |
109 | } | |
110 | } | |
111 | ||
112 | void print_line(GList *line) | |
113 | { | |
114 | int line_number = g_list_position(first_line, line); | |
115 | GNode *file = NODE(line); | |
116 | int depth = g_node_depth(file) - 1; | |
117 | char *link_str; | |
118 | ||
119 | wmove(tree_window, line_number, 0); | |
120 | wclrtoeol(tree_window); | |
121 | ||
122 | if (line == selected_line) | |
123 | wattron(tree_window, A_REVERSE); | |
124 | ||
125 | if (G_NODE_IS_ROOT(file)) { | |
126 | wattron(tree_window, A_BOLD); | |
127 | waddnstr(tree_window, FILE(file)->name, getmaxx(tree_window) - 1); | |
128 | if (!g_str_has_suffix(FILE(file)->name, "/")) | |
129 | waddch(tree_window, '/'); | |
130 | wattroff(tree_window, A_BOLD); | |
131 | } else { | |
132 | if (file != g_node_last_sibling(file)) | |
133 | mvwaddch(tree_window, line_number, 2 * depth - 2, ACS_LTEE); | |
134 | else | |
135 | mvwaddch(tree_window, line_number, 2 * depth - 2, ACS_LLCORNER); | |
136 | ||
137 | waddch(tree_window, ACS_HLINE); | |
138 | if (FILE(file)->link == TRUE) { | |
139 | link_str = g_strdup_printf("%s -> %s", FILE(file)->name, | |
140 | FILE(file)->link_path); | |
141 | waddnstr(tree_window, link_str, getmaxx(tree_window) - 2 * depth - 1); | |
142 | free(link_str); | |
143 | } else | |
144 | waddnstr(tree_window, FILE(file)->name, | |
145 | getmaxx(tree_window) - 2 * depth - 1); | |
146 | ||
147 | if (FILE(file)->type == directory_type && ((FILE(file)->link == TRUE && | |
148 | !g_str_has_suffix(FILE(file)->link_path, "/")) || | |
149 | (FILE(file)->link == FALSE && | |
150 | !g_str_has_suffix(FILE(file)->name, "/")))) | |
151 | waddch(tree_window, '/'); | |
152 | print_parents_lines(line); | |
153 | } | |
154 | wattroff(tree_window, A_REVERSE); | |
155 | } | |
156 | ||
157 | void clear_info(void) | |
158 | { | |
159 | werase(info_window); | |
160 | } | |
161 | ||
162 | void print_info(char *message, gboolean bold) | |
163 | { | |
164 | static char *last_message = NULL; | |
165 | static gboolean last_bold = FALSE; | |
166 | ||
167 | if (message != NULL) { | |
168 | if (last_message != NULL) | |
169 | free(last_message); | |
170 | last_message = strdup(message); | |
171 | } | |
172 | if (bold == TRUE || bold == FALSE) | |
173 | last_bold = bold; | |
174 | if (last_bold == TRUE) | |
175 | wattron(info_window, A_BOLD); | |
176 | werase(info_window); | |
177 | if (last_message != NULL) | |
178 | waddnstr(info_window, last_message, getmaxx(info_window)); | |
179 | wattroff(info_window, A_BOLD); | |
180 | } | |
181 | ||
182 | void print_error_info(int last_errno, char *file, int line, char *message) | |
183 | { | |
184 | char *message_format = "Error (%s:%i): %s"; | |
185 | char *message_str; | |
186 | ||
187 | if (message != NULL) | |
188 | message_str = g_strdup_printf(message_format, file, line, message); | |
189 | else | |
190 | message_str = g_strdup_printf(message_format, file, line, strerror(last_errno)); | |
191 | print_info(message_str, TRUE); | |
192 | free(message_str); | |
193 | } | |
194 | ||
195 | void update_last_line(void) | |
196 | { | |
197 | if (g_list_length(first_line) > getmaxy(tree_window)) | |
198 | last_line = g_list_nth(first_line, getmaxy(tree_window) - 1); | |
199 | else | |
200 | last_line = g_list_last(lines); | |
201 | } | |
202 | ||
203 | void add_directory_content(GNode *directory) | |
204 | { | |
205 | GNode *file_ptr, *directory_sibling; | |
206 | int position; | |
207 | ||
208 | directory_sibling = get_next_file_not_deepper(directory); | |
209 | ||
210 | for (position = g_list_position(lines, FILE(directory)->line) + 1, | |
211 | file_ptr = g_node_first_child(directory); | |
212 | file_ptr != NULL && file_ptr != directory_sibling; | |
213 | position++, file_ptr = get_next_file(file_ptr)) { | |
214 | lines = g_list_insert(lines, file_ptr, position); | |
215 | FILE(file_ptr)->line = g_list_nth(lines, position); | |
216 | } | |
217 | } | |
218 | ||
219 | void open_directory(GNode *directory) | |
220 | { | |
221 | if (FILE(directory)->read == FALSE && read_directory(directory) == FALSE) | |
222 | return; | |
223 | ||
224 | FILE(directory)->open = TRUE; | |
225 | add_directory_content(directory); | |
226 | update_last_line(); | |
227 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
228 | } | |
229 | ||
230 | void close_directory(GNode *directory) | |
231 | { | |
232 | GNode *directory_sibling; | |
233 | GList *line_ptr; | |
234 | ||
235 | directory_sibling = get_next_file_not_deepper(directory); | |
236 | ||
237 | for (line_ptr = g_list_next(FILE(directory)->line); line_ptr != NULL && | |
238 | NODE(line_ptr) != directory_sibling; line_ptr = g_list_next(FILE(directory)->line)) | |
239 | lines = g_list_delete_link(lines, line_ptr); | |
240 | ||
241 | FILE(directory)->open = FALSE; | |
242 | update_last_line(); | |
243 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
244 | } | |
245 | ||
246 | void update_directory(GNode *directory) | |
247 | { | |
248 | close_directory(directory); | |
249 | destroy_directory_content(directory); | |
250 | open_directory(directory); | |
251 | ||
252 | if (FILE(directory)->show_dotfiles == TRUE) | |
253 | show_dotfiles(directory); | |
254 | } | |
255 | ||
256 | void show_dotfiles(GNode *directory) | |
257 | { | |
258 | add_dotfiles(directory); | |
259 | update_last_line(); | |
260 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
261 | } | |
262 | ||
263 | void hide_dotfiles(GNode *directory) | |
264 | { | |
265 | remove_dotfiles(directory); | |
266 | update_last_line(); | |
267 | print_lines(g_list_next(FILE(directory)->line), last_line, TRUE); | |
268 | } | |
269 | ||
270 | void select_line(GList *line) | |
271 | { | |
272 | select_nth_line(g_list_position(lines, line) - g_list_position(lines, selected_line)); | |
273 | } | |
274 | ||
275 | void select_file(GNode *file) | |
276 | { | |
277 | select_line(FILE(file)->line); | |
278 | } | |
279 | ||
280 | void select_nth_line(int n_lines) | |
281 | { | |
282 | GList *old_selected_line; | |
283 | int min_n_lines, max_n_lines; | |
284 | ||
285 | if ((max_n_lines = g_list_length(selected_line) - 1) < n_lines) | |
286 | n_lines = max_n_lines; | |
287 | else if((min_n_lines = (int) g_list_length(selected_line) - | |
288 | (int) g_list_length(lines)) > n_lines) | |
289 | n_lines = min_n_lines; | |
290 | ||
291 | old_selected_line = selected_line; | |
292 | if (n_lines < 0) | |
293 | selected_line = g_list_nth(lines, g_list_position(lines, selected_line) + n_lines); | |
294 | else if (n_lines > 0) | |
295 | selected_line = g_list_nth(selected_line, n_lines); | |
296 | ||
297 | if (g_list_position(lines, selected_line) < g_list_position(lines, first_line)) | |
298 | scroll_tree(g_list_position(lines, selected_line) - | |
299 | g_list_position(lines, first_line)); | |
300 | else if (g_list_position(lines, selected_line) > g_list_position(lines, last_line)) | |
301 | scroll_tree(g_list_position(lines, selected_line) - | |
302 | g_list_position(lines, last_line)); | |
303 | ||
304 | if (n_lines != 0) { | |
305 | print_line(selected_line); | |
306 | if (g_list_position(lines, first_line) <= g_list_position(lines, old_selected_line) && | |
307 | g_list_position(lines, old_selected_line) <= | |
308 | g_list_position(lines, last_line)) | |
309 | print_line(old_selected_line); | |
310 | } | |
311 | } | |
312 | ||
313 | void scroll_tree(int n_lines) | |
314 | { | |
315 | int min_n_lines, max_n_lines; | |
316 | ||
317 | if ((min_n_lines = -g_list_position(lines, first_line)) > n_lines) | |
318 | n_lines = min_n_lines; | |
319 | else if ((max_n_lines = g_list_length(last_line) - 1) < n_lines) | |
320 | n_lines = max_n_lines; | |
321 | ||
322 | wscrl(tree_window, n_lines); | |
323 | if (n_lines < 0) { | |
324 | first_line = g_list_nth(lines, g_list_position(lines, first_line) + n_lines); | |
325 | update_last_line(); | |
326 | print_lines(first_line, g_list_nth(first_line, -n_lines), FALSE); | |
327 | } else if (n_lines > 0) { | |
328 | first_line = g_list_nth(first_line, n_lines); | |
329 | update_last_line(); | |
330 | print_lines(g_list_nth(lines, g_list_position(lines, last_line) - n_lines), | |
331 | last_line, TRUE); | |
332 | } | |
333 | } | |
334 | ||
335 | void select_line_inside_window(void) | |
336 | { | |
337 | if (g_list_position(lines, selected_line) < g_list_position(lines, first_line)) | |
338 | select_line(first_line); | |
339 | else if (g_list_position(lines, selected_line) > g_list_position(lines, last_line)) | |
340 | select_line(last_line); | |
341 | } | |
342 | ||
343 | void select_next_line_by_first_letter(char letter) | |
344 | { | |
345 | GList *line_ptr; | |
346 | ||
347 | for (line_ptr = g_list_next(selected_line); line_ptr != NULL; | |
348 | line_ptr = g_list_next(line_ptr)) { | |
349 | if (FILE(NODE(line_ptr))->name[0] == letter) { | |
350 | select_line(line_ptr); | |
351 | return; | |
352 | } | |
353 | } | |
354 | } | |
355 | ||
356 | void select_previous_line_by_first_letter(char letter) | |
357 | { | |
358 | GList *line_ptr; | |
359 | ||
360 | for (line_ptr = g_list_previous(selected_line); line_ptr != NULL; | |
361 | line_ptr = g_list_previous(line_ptr)) { | |
362 | if (FILE(NODE(line_ptr))->name[0] == letter) { | |
363 | select_line(line_ptr); | |
364 | return; | |
365 | } | |
366 | } | |
367 | } |