1 /*
2  * Copyright (C) 2005 Sasha Vasko <sasha at aftercode.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19 
20 #define LOCAL_DEBUG
21 #include "../configure.h"
22 
23 #include "../libAfterStep/asapp.h"
24 #include "../libAfterStep/screen.h"
25 #include "../libAfterImage/afterimage.h"
26 
27 #include <unistd.h>
28 
29 #include "asgtk.h"
30 #include "asgtkai.h"
31 #include "asgtkdirtree.h"
32 
33 #define TREE_NAME		0
34 #define TREE_FULLPATH   1
35 #define TREE_SCANNED	2
36 #define TREE_COLUMNS	3
37 
38 
39 /*  local function prototypes  */
40 static void asgtk_dir_tree_class_init (ASGtkDirTreeClass * klass);
41 static void asgtk_dir_tree_init (ASGtkDirTree * iv);
42 static void asgtk_dir_tree_dispose (GObject * object);
43 static void asgtk_dir_tree_finalize (GObject * object);
44 static void asgtk_dir_tree_style_set (GtkWidget * widget,
45 																			GtkStyle * prev_style);
46 static void asgtk_dir_tree_refresh_child (GtkTreeStore * dir_store,
47 																					GtkTreeIter * parent,
48 																					char *fullchildname, int level);
49 
50 
51 /*  private variables  */
52 static GtkScrolledWindowClass *parent_class = NULL;
53 
asgtk_dir_tree_get_type(void)54 GType asgtk_dir_tree_get_type (void)
55 {
56 	static GType id_type = 0;
57 
58 	if (!id_type) {
59 		static const GTypeInfo id_info = {
60 			sizeof (ASGtkDirTreeClass),
61 			(GBaseInitFunc) NULL,
62 			(GBaseFinalizeFunc) NULL,
63 			(GClassInitFunc) asgtk_dir_tree_class_init,
64 			NULL,											/* class_finalize */
65 			NULL,											/* class_data     */
66 			sizeof (ASGtkDirTree),
67 			0,												/* n_preallocs    */
68 			(GInstanceInitFunc) asgtk_dir_tree_init,
69 		};
70 
71 		id_type =
72 				g_type_register_static (GTK_TYPE_SCROLLED_WINDOW, "ASGtkDirTree",
73 																&id_info, 0);
74 	}
75 
76 	return id_type;
77 }
78 
asgtk_dir_tree_class_init(ASGtkDirTreeClass * klass)79 static void asgtk_dir_tree_class_init (ASGtkDirTreeClass * klass)
80 {
81 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
82 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
83 
84 	parent_class = g_type_class_peek_parent (klass);
85 
86 	object_class->dispose = asgtk_dir_tree_dispose;
87 	object_class->finalize = asgtk_dir_tree_finalize;
88 
89 	widget_class->style_set = asgtk_dir_tree_style_set;
90 
91 }
92 
asgtk_dir_tree_init(ASGtkDirTree * id)93 static void asgtk_dir_tree_init (ASGtkDirTree * id)
94 {
95 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (id),
96 																	GTK_POLICY_AUTOMATIC,
97 																	GTK_POLICY_AUTOMATIC);
98 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (id),
99 																			 GTK_SHADOW_IN);
100 	id->root = NULL;
101 }
102 
asgtk_dir_tree_dispose(GObject * object)103 static void asgtk_dir_tree_dispose (GObject * object)
104 {
105 	ASGtkDirTree *dt = ASGTK_DIR_TREE (object);
106 
107 	destroy_string (&(dt->root));
108 
109 	G_OBJECT_CLASS (parent_class)->dispose (object);
110 }
111 
asgtk_dir_tree_finalize(GObject * object)112 static void asgtk_dir_tree_finalize (GObject * object)
113 {
114 	G_OBJECT_CLASS (parent_class)->finalize (object);
115 }
116 
117 static void
asgtk_dir_tree_style_set(GtkWidget * widget,GtkStyle * prev_style)118 asgtk_dir_tree_style_set (GtkWidget * widget, GtkStyle * prev_style)
119 {
120 	/* ASGtkDirTree *id = ASGTK_DIR_TREE (widget); */
121 
122 	GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
123 }
124 
125 static void
asgtk_dir_tree_sel_handler(GtkTreeSelection * selection,gpointer user_data)126 asgtk_dir_tree_sel_handler (GtkTreeSelection * selection,
127 														gpointer user_data)
128 {
129 	ASGtkDirTree *dt = ASGTK_DIR_TREE (user_data);
130 	GtkTreeIter iter;
131 	GtkTreeModel *model;
132 
133 	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
134 		gtk_tree_model_get (model, &iter, TREE_FULLPATH, &(dt->curr_selection),
135 												-1);
136 	} else {
137 		dt->curr_selection = NULL;
138 	}
139 
140 	if (dt->sel_change_handler)
141 		dt->sel_change_handler (dt, dt->sel_change_user_data);
142 }
143 
144 static void
asgtk_dir_tree_expand_handler(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * path,gpointer user_data)145 asgtk_dir_tree_expand_handler (GtkTreeView * treeview, GtkTreeIter * iter,
146 															 GtkTreePath * path, gpointer user_data)
147 {
148 	ASGtkDirTree *dt = ASGTK_DIR_TREE (user_data);
149 	GtkTreeModel *model = dt->tree_model;
150 	GtkTreeIter child_iter;
151 
152 	gtk_tree_path_down (path);
153 	gtk_tree_model_get_iter (model, &child_iter, path);
154 
155 	do {
156 		char *fullpath;
157 		int scanned = 0;
158 
159 		gtk_tree_model_get (model, &child_iter, TREE_FULLPATH, &fullpath,
160 												TREE_SCANNED, &scanned, -1);
161 		if (scanned == 1)
162 			break;
163 
164 		asgtk_dir_tree_refresh_child (GTK_TREE_STORE (model), &child_iter,
165 																	fullpath, 1);
166 		gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter, TREE_SCANNED,
167 												1, -1);
168 	}
169 	while (gtk_tree_model_iter_next (model, &child_iter));
170 }
171 
172 /*  public functions  */
asgtk_dir_tree_new()173 GtkWidget *asgtk_dir_tree_new ()
174 {
175 	ASGtkDirTree *dt;
176 	GtkTreeSelection *selection;
177 
178 	dt = g_object_new (ASGTK_TYPE_DIR_TREE, NULL);
179 
180 	dt->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
181 	dt->tree_model =
182 			GTK_TREE_MODEL (gtk_tree_store_new
183 											(TREE_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
184 											 G_TYPE_UINT));
185 
186 	gtk_container_add (GTK_CONTAINER (dt), GTK_WIDGET (dt->tree_view));
187 	gtk_tree_view_set_model (dt->tree_view, dt->tree_model);
188 	gtk_widget_show (GTK_WIDGET (dt->tree_view));
189 	dt->cell = gtk_cell_renderer_text_new ();
190 
191 	dt->column =
192 			gtk_tree_view_column_new_with_attributes ("", dt->cell, "text", 0,
193 																								NULL);
194 	gtk_tree_view_append_column (dt->tree_view,
195 															 GTK_TREE_VIEW_COLUMN (dt->column));
196 
197 	selection = gtk_tree_view_get_selection (dt->tree_view);
198 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
199 	g_signal_connect (selection, "changed",
200 										G_CALLBACK (asgtk_dir_tree_sel_handler), dt);
201 	g_signal_connect (dt->tree_view, "row-expanded",
202 										G_CALLBACK (asgtk_dir_tree_expand_handler), dt);
203 
204 	colorize_gtk_tree_view_window (GTK_WIDGET (dt));
205 	LOCAL_DEBUG_OUT ("created image ASGtkDirTree object %p", dt);
206 	return GTK_WIDGET (dt);
207 }
208 
209 void
asgtk_dir_tree_set_root(ASGtkDirTree * dt,char * root,GtkTreeModel * saved_model)210 asgtk_dir_tree_set_root (ASGtkDirTree * dt, char *root,
211 												 GtkTreeModel * saved_model)
212 {
213 	GtkTreeModel *old_model;
214 
215 	g_return_if_fail (ASGTK_IS_DIR_TREE (dt));
216 
217 	if (dt->root == NULL && root == NULL)
218 		return;
219 	if (dt->root && root && strcmp (dt->root, root) == 0)
220 		return;
221 	destroy_string (&(dt->root));
222 
223 	if (root)
224 		dt->root = mystrdup (root);
225 
226 	dt->curr_selection = NULL;
227 	old_model = GTK_TREE_MODEL (dt->tree_model);
228 
229 	if (saved_model != NULL)
230 		dt->tree_model = g_object_ref (saved_model);
231 	else
232 		dt->tree_model =
233 				GTK_TREE_MODEL (gtk_tree_store_new
234 												(TREE_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
235 												 G_TYPE_UINT));
236 	gtk_tree_view_set_model (dt->tree_view, dt->tree_model);
237 
238 	asgtk_dir_tree_refresh (dt);
239 	g_object_unref (old_model);
240 
241 }
242 
asgtk_dir_tree_get_model(ASGtkDirTree * dt)243 GtkTreeModel *asgtk_dir_tree_get_model (ASGtkDirTree * dt)
244 {
245 	return g_object_ref (dt->tree_model);
246 }
247 
asgtk_dir_tree_set_title(ASGtkDirTree * dt,const gchar * title)248 void asgtk_dir_tree_set_title (ASGtkDirTree * dt, const gchar * title)
249 {
250 	g_return_if_fail (ASGTK_IS_DIR_TREE (dt));
251 	gtk_tree_view_column_set_title (dt->column, title);
252 }
253 
254 void
asgtk_dir_tree_set_sel_handler(ASGtkDirTree * dt,_ASGtkDirTree_sel_handler sel_change_handler,gpointer user_data)255 asgtk_dir_tree_set_sel_handler (ASGtkDirTree * dt,
256 																_ASGtkDirTree_sel_handler
257 																sel_change_handler, gpointer user_data)
258 {
259 	g_return_if_fail (ASGTK_IS_DIR_TREE (dt));
260 
261 	dt->sel_change_handler = sel_change_handler;
262 	dt->sel_change_user_data = user_data;
263 
264 }
265 
asgtk_dir_tree_get_selection(ASGtkDirTree * dt)266 char *asgtk_dir_tree_get_selection (ASGtkDirTree * dt)
267 {
268 	char *result = NULL;
269 
270 	if (ASGTK_IS_DIR_TREE (dt))
271 		result = mystrdup (dt->curr_selection);
272 
273 	return result;
274 }
275 
276 static void
asgtk_dir_tree_refresh_child(GtkTreeStore * dir_store,GtkTreeIter * parent,char * fullchildname,int level)277 asgtk_dir_tree_refresh_child (GtkTreeStore * dir_store,
278 															GtkTreeIter * parent, char *fullchildname,
279 															int level)
280 {
281 	if (--level >= 0) {
282 		GtkTreeIter iter;
283 		struct direntry **list = NULL;
284 		int n = my_scandir (fullchildname, &list, NULL, NULL);
285 
286 		if (n > 0) {
287 			int i;
288 
289 			for (i = 0; i < n; i++) {
290 				if (S_ISDIR (list[i]->d_mode)) {
291 					Bool skip = False;
292 
293 					if (list[i]->d_name[0] == '.') {
294 						skip = (list[i]->d_name[1] == '\0' ||
295 										(list[i]->d_name[1] == '.'
296 										 && list[i]->d_name[2] == '\0'));
297 					}
298 					if (!skip) {
299 						char *fulldirname =
300 								make_file_name (fullchildname, list[i]->d_name);
301 
302 						gtk_tree_store_append (dir_store, &iter, parent);
303 						gtk_tree_store_set (dir_store, &iter, TREE_NAME,
304 																list[i]->d_name, TREE_FULLPATH,
305 																fulldirname, TREE_SCANNED, 0, -1);
306 						if (level > 0)
307 							asgtk_dir_tree_refresh_child (dir_store, &iter, fulldirname,
308 																						level - 1);
309 						free (fulldirname);
310 					}
311 				}
312 				free (list[i]);
313 			}
314 			free (list);
315 		}
316 	}
317 }
318 
319 
asgtk_dir_tree_refresh(ASGtkDirTree * dt)320 void asgtk_dir_tree_refresh (ASGtkDirTree * dt)
321 {
322 	GtkTreeStore *dir_store;
323 	GtkTreeIter iter;
324 
325 	g_return_if_fail (ASGTK_IS_DIR_TREE (dt));
326 
327 	dir_store = GTK_TREE_STORE (dt->tree_model);
328 
329 	gtk_tree_store_clear (dir_store);
330 
331 	if (dt->root != NULL) {
332 		gtk_tree_store_append (dir_store, &iter, NULL);
333 		gtk_tree_store_set (dir_store, &iter, TREE_NAME, dt->root,
334 												TREE_FULLPATH, dt->root, TREE_SCANNED, 1, -1);
335 		asgtk_dir_tree_refresh_child (dir_store, &iter, dt->root, 2);
336 	}
337 #if 0
338 	curr_sel = mystrdup (dt->curr_selection ? dt->curr_selection->name : "");
339 
340 	gtk_list_store_clear (GTK_LIST_STORE (dt->tree_model));
341 	destroy_asimage_list (&(dt->entries));
342 	dt->curr_selection = NULL;
343 	if (dt->fulldirname) {
344 		int count;
345 		GtkTreeIter iter;
346 		ASImageListEntry *curr;
347 		int mini_ext_len =
348 				dt->mini_extension ? strlen (dt->mini_extension) : 0;
349 
350 		dt->entries =
351 				get_asimage_list (get_screen_visual (NULL), dt->fulldirname, 0,
352 													get_screen_image_manager (NULL)->gamma, 0, 0, 0,
353 													&count, NULL);
354 
355 		curr = dt->entries;
356 		while (curr) {
357 			Bool mini = False;
358 
359 			LOCAL_DEBUG_OUT ("adding item \"%s\"", curr->name);
360 			if (mini_ext_len > 1) {
361 				if (dt->mini_extension[mini_ext_len - 1] == '.')
362 					mini =
363 							(strncmp (curr->name, dt->mini_extension, mini_ext_len) ==
364 							 0);
365 				else {
366 					int name_len = strlen (curr->name);
367 
368 					if (name_len > mini_ext_len)
369 						mini =
370 								(strncmp
371 								 (&(curr->name[name_len - mini_ext_len]),
372 									dt->mini_extension, mini_ext_len) == 0);
373 				}
374 			}
375 			if (!mini && curr->type <= ASIT_Supported) {
376 				gtk_list_store_append (GTK_LIST_STORE (dt->tree_model), &iter);
377 				gtk_list_store_set (GTK_LIST_STORE (dt->tree_model), &iter, 0,
378 														curr->name, 1, curr, -1);
379 				if (++items == 1)
380 					gtk_tree_selection_select_iter (gtk_tree_view_get_selection
381 																					(dt->tree_view), &iter);
382 				else if (strcmp (curr->name, curr_sel) == 0)
383 					gtk_tree_selection_select_iter (gtk_tree_view_get_selection
384 																					(dt->tree_view), &iter);
385 			}
386 			curr = curr->next;
387 		}
388 		/*
389 		   This section doesnt seem to be needed here... ?
390 		   :sG:
391 		   The following 3 lines make the file lists sorted by
392 		   the File Name, Ascending.
393 
394 		   For general file listing (not dirs)
395 		   GtkTreeSortable *sortable;
396 		   sortable = GTK_TREE_SORTABLE(dt->tree_model);
397 		   gtk_tree_sortable_set_sort_column_id(sortable,1,GTK_SORT_ASCENDING);
398 		 */
399 
400 	}
401 	if (curr_sel)
402 		free (curr_sel);
403 	if (items == 0) {
404 		asgtk_dir_tree_sel_handler (gtk_tree_view_get_selection
405 																(dt->tree_view), dt);
406 	}
407 #endif
408 }
409 
asgtk_dir_tree_get_curr_path(ASGtkDirTree * dt)410 GtkTreePath *asgtk_dir_tree_get_curr_path (ASGtkDirTree * dt)
411 {
412 	GtkTreePath *path = NULL;
413 	GtkTreeViewColumn *focus_column = NULL;
414 
415 	gtk_tree_view_get_cursor (dt->tree_view, &path, &focus_column);
416 	return path;
417 }
418 
419 void
asgtk_dir_tree_restore_curr_path(ASGtkDirTree * dt,GtkTreePath * path)420 asgtk_dir_tree_restore_curr_path (ASGtkDirTree * dt, GtkTreePath * path)
421 {
422 	if (path) {
423 		gtk_tree_view_expand_to_path (dt->tree_view, path);
424 		gtk_tree_view_set_cursor (dt->tree_view, path, dt->column, FALSE);
425 	}
426 }
427