Add VIM modelines to new files
[geeqie.git] / src / filecluster.c
1 /*
2  * Copyright (C) 2008 - 2016 The Geeqie Team
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 along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include "filecluster.h"
20
21 #include "filedata.h"
22
23 static gboolean check_list_contains_sublist(GList *haystack, GList *needle)
24 {
25         // TODO(xsdg): Optimize this!  Sort, then scan?
26         GList *h_work, *n_work;
27         for (n_work = needle; n_work; n_work = n_work->next)
28         {
29                 gboolean found = FALSE;
30                 for (h_work = haystack; h_work; h_work = h_work->next)
31                 {
32                         if (n_work == h_work)
33                                 {
34                                 found = TRUE;
35                                 break;
36                                 }
37                 }
38
39                 if (!found) return FALSE;
40         }
41
42         return TRUE;
43 }
44
45 static gboolean filecluster_fd_equal(gconstpointer ptr_a, gconstpointer ptr_b)
46 {
47         // TODO(xsdg): Is there anything we can/should do to validate inputs?
48         FileData *fd_a = (FileData *)ptr_a;
49         FileData *fd_b = (FileData *)ptr_b;
50         return !filelist_sort_compare_filedata(fd_a, fd_b);
51 }
52
53 // TODO(xsdg): Move this into filedata.h
54 static guint filecluster_fd_hash(gconstpointer ptr)
55 {
56         if (!ptr) return 1;
57         FileData *fd = (FileData *)ptr;
58         return 7 * g_str_hash(fd->original_path);
59 }
60
61 FileClusterList *fileclusterlist_new()
62 {
63         FileClusterList *fcl = g_new0(FileClusterList, 1);
64         fcl->clusters = g_hash_table_new(&filecluster_fd_hash, &filecluster_fd_equal);
65         return fcl;
66 }
67
68 FileCluster *filecluster_new()
69 {
70         FileCluster *fc = g_new0(FileCluster, 1);
71         fc->show_children = FALSE;
72         return fc;
73 }
74
75 void fileclusterlist_free(FileClusterList *fcl)
76 {
77         // TODO(xsdg): don't leak stuff
78         // if (fcl->fd_list) g_list_free_full(fcl->fd_list, (GDestroyNotify)&filecluster_free);
79         g_hash_table_destroy(fcl->clusters);
80         g_free(fcl);
81 }
82
83 void filecluster_free(FileCluster *fc)
84 {
85         filelist_free(fc->items);
86         g_free(fc);
87 }
88
89 gboolean filecluster_toggle_show_children(FileCluster *fc)
90 {
91         fc->show_children = !fc->show_children;
92         return fc->show_children;
93 }
94
95 FileCluster *fileclusterlist_create_cluster(FileClusterList *fcl, GList *fd_items)
96 {
97         GList *work;
98
99         // Check preconditions.
100         if (!fd_items) return NULL;
101         for (work = fd_items; work; work = work->next)
102                 {
103                 FileData *fd = work->data;
104                 if (g_hash_table_contains(fcl->clusters, fd))
105                         {
106                         // TODO(xsdg): Show this warning in the UI.
107                         g_warning("Tried to create a cluster with a file that is already clustered.");
108                         return NULL;
109                         }
110                 }
111
112         FileCluster *new_fc = filecluster_new();
113         new_fc->items = filelist_copy(fd_items);
114         new_fc->head = new_fc->items;
115
116         for (GList *item = new_fc->items; item; item = item->next)
117                 {
118                 FileData *fd = item->data;
119                 g_hash_table_insert(fcl->clusters, fd, new_fc);
120                 }
121
122         return new_fc;
123 }
124
125 gboolean filecluster_has_head(FileCluster *fc, FileData *fd)
126 {
127         if (!fd) return FALSE;
128         return filecluster_fd_equal(fc->head->data, fd);
129 }
130
131 gboolean filecluster_has_child(FileCluster *fc, FileData *fd)
132 {
133         if (!fd) return FALSE;
134         return !filecluster_fd_equal(fc->head->data, fd);
135 }
136
137 gboolean fileclusterlist_has_head(FileClusterList *fcl, FileData *fd)
138 {
139         FileCluster *fc = g_hash_table_lookup(fcl->clusters, fd);
140         if (!fc) return FALSE;
141         return filecluster_has_head(fc, fd);
142 }
143
144 gboolean fileclusterlist_has_child(FileClusterList *fcl, FileData *fd)
145 {
146         FileCluster *fc = g_hash_table_lookup(fcl->clusters, fd);
147         if (!fc) return FALSE;
148         return filecluster_has_child(fc, fd);
149 }
150
151 static gboolean fileclusterlist_should_hide(FileClusterList *fcl, FileData *fd)
152 {
153         FileCluster *fc = g_hash_table_lookup(fcl->clusters, fd);
154         if (!fc) return FALSE;
155         // Only difference vs. fileclusterlist_has_child.  Basically, if the node is a child, but
156         // we're showing children, then don't hide.
157         if (fc->show_children) return FALSE;
158         return filecluster_has_child(fc, fd);
159 }
160
161 // TODO(xsdg): pick a better name for this function
162 GList *fileclusterlist_next_non_child(FileClusterList *fcl, GList *list)
163 {
164         // Check for no-ops
165         if (!list || !g_hash_table_size(fcl->clusters)) return list;
166
167         // Clusters are being used, so we have to actually check things.
168         for (; list; list = list->next)
169         {
170                 FileData *fd = list->data;
171                 if (!fileclusterlist_has_child(fcl, fd)) return list;
172         }
173
174         return NULL;
175 }
176
177 GList *fileclusterlist_remove_children_from_list(FileClusterList *fcl, GList *list)
178 {
179         GList *work = list;
180
181         while (work)
182         {
183                 FileData *fd = work->data;
184                 GList *link = work;
185                 // Advance early in case link needs to be removed/freed.
186                 work = work->next;
187
188                 if (fileclusterlist_should_hide(fcl, fd))
189                 {
190                         list = g_list_remove_link(list, link);
191                         file_data_unref(fd);
192                         g_list_free(link);
193                 }
194         }
195
196         return list;
197 }
198
199 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */