chiark / gitweb /
Most of intro section
[disorder] / disobedience / choose-menu.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2008 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file disobedience/choose-menu.c
19  * @brief Popup menu for choose screen
20  */
21 #include "disobedience.h"
22 #include "popup.h"
23 #include "choose.h"
24
25 /** @brief Popup menu */
26 static GtkWidget *choose_menu;
27
28 /** @brief Path to directory pending a "select children" operation */
29 static GtkTreePath *choose_eventually_select_children;
30
31 /** @brief Should edit->select all be sensitive?  No, for the choose tab. */
32 static int choose_selectall_sensitive(void attribute((unused)) *extra) {
33   return FALSE;
34 }
35
36 /** @brief Activate edit->select all (which should do nothing) */
37 static void choose_selectall_activate(GtkMenuItem attribute((unused)) *item,
38                                       gpointer attribute((unused)) userdata) {
39 }
40
41 /** @brief Should 'select none' be sensitive
42  *
43  * Yes if anything is selected.
44  */
45 static int choose_selectnone_sensitive(void attribute((unused)) *extra) {
46   return gtk_tree_selection_count_selected_rows(choose_selection) > 0;
47 }
48
49 /** @brief Activate select none */
50 static void choose_selectnone_activate(GtkMenuItem attribute((unused)) *item,
51                                        gpointer attribute((unused)) userdata) {
52   gtk_tree_selection_unselect_all(choose_selection);
53 }
54
55 static void choose_play_sensitive_callback(GtkTreeModel attribute((unused)) *model,
56                                            GtkTreePath attribute((unused)) *path,
57                                            GtkTreeIter *iter,
58                                            gpointer data) {
59   int *filesp = data;
60
61   if(*filesp == -1)
62     return;
63   if(choose_is_dir(iter))
64     *filesp = -1;
65   else if(choose_is_file(iter))
66     ++*filesp;
67 }
68
69 /** @brief Should 'play' be sensitive?
70  *
71  * Yes if tracks are selected and no directories are */
72 static int choose_play_sensitive(void attribute((unused)) *extra) {
73   int files = 0;
74   
75   gtk_tree_selection_selected_foreach(choose_selection,
76                                       choose_play_sensitive_callback,
77                                       &files);
78   return files > 0;
79 }
80
81 static void choose_gather_selected_files_callback(GtkTreeModel attribute((unused)) *model,
82                                                   GtkTreePath attribute((unused)) *path,
83                                                   GtkTreeIter *iter,
84                                                   gpointer data) {
85   struct vector *v = data;
86
87   if(choose_is_file(iter))
88     vector_append(v, choose_get_track(iter));
89 }
90
91 static void choose_gather_selected_dirs_callback(GtkTreeModel attribute((unused)) *model,
92                                                  GtkTreePath attribute((unused)) *path,
93                                                  GtkTreeIter *iter,
94                                                  gpointer data) {
95   struct vector *v = data;
96
97   if(choose_is_dir(iter))
98     vector_append(v, choose_get_track(iter));
99 }
100
101   
102 static void choose_play_activate(GtkMenuItem attribute((unused)) *item,
103                                  gpointer attribute((unused)) userdata) {
104   struct vector v[1];
105   vector_init(v);
106   gtk_tree_selection_selected_foreach(choose_selection,
107                                       choose_gather_selected_files_callback,
108                                       v);
109   for(int n = 0; n < v->nvec; ++n)
110     disorder_eclient_play(client, v->vec[n], choose_play_completed, 0);
111 }
112   
113 static int choose_properties_sensitive(void *extra) {
114   return choose_play_sensitive(extra);
115 }
116   
117 static void choose_properties_activate(GtkMenuItem attribute((unused)) *item,
118                                        gpointer attribute((unused)) userdata) {
119   struct vector v[1];
120   vector_init(v);
121   gtk_tree_selection_selected_foreach(choose_selection,
122                                       choose_gather_selected_files_callback,
123                                       v);
124   properties(v->nvec, (const char **)v->vec, toplevel);
125 }
126
127 /** @brief Set sensitivity for select children
128  *
129  * Sensitive if we've selected exactly one directory.
130  */
131 static int choose_selectchildren_sensitive(void attribute((unused)) *extra) {
132   struct vector v[1];
133   /* Only one thing should be selected */
134   if(gtk_tree_selection_count_selected_rows(choose_selection) != 1)
135     return FALSE;
136   /* The selected thing should be a directory */
137   vector_init(v);
138   gtk_tree_selection_selected_foreach(choose_selection,
139                                       choose_gather_selected_dirs_callback,
140                                       v);
141   return v->nvec == 1;
142 }
143
144 /** @brief Actually select the children of path
145  *
146  * We deselect everything else, too.
147  */
148 static void choose_select_children(GtkTreePath *path) {
149   GtkTreeIter iter[1], child[1];
150   
151   if(gtk_tree_model_get_iter(GTK_TREE_MODEL(choose_store), iter, path)) {
152     gtk_tree_selection_unselect_all(choose_selection);
153     for(int n = 0;
154         gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(choose_store), child,
155                                       iter, n);
156         ++n) {
157       if(choose_is_file(child))
158         gtk_tree_selection_select_iter(choose_selection, child);
159     }
160   }
161 }
162
163 /** @brief Called to expand the children of path/iter */
164 static void choose_selectchildren_callback(GtkTreeModel attribute((unused)) *model,
165                                            GtkTreePath *path,
166                                            GtkTreeIter attribute((unused)) *iter,
167                                            gpointer attribute((unused)) data) {
168   if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(choose_view), path)) {
169     /* Directory is already expanded */
170     choose_select_children(path);
171   } else {
172     /* Directory is not expanded, so expand it */
173     gtk_tree_view_expand_row(GTK_TREE_VIEW(choose_view), path, FALSE/*!expand_all*/);
174     /* Select its children when it's done */
175     if(choose_eventually_select_children)
176       gtk_tree_path_free(choose_eventually_select_children);
177     choose_eventually_select_children = gtk_tree_path_copy(path);
178   }
179 }
180
181 /** @brief Called when all pending track fetches are finished
182  *
183  * If there's a pending select-children operation, it can now be actioned
184  * (or might have gone stale).
185  */
186 void choose_menu_moretracks(const char attribute((unused)) *event,
187                             void attribute((unused)) *eventdata,
188                             void attribute((unused)) *callbackdata) {
189   if(choose_eventually_select_children) {
190     choose_select_children(choose_eventually_select_children);
191     gtk_tree_path_free(choose_eventually_select_children);
192     choose_eventually_select_children = 0;
193   }
194 }
195
196 /** @brief Select all children
197  *
198  * Easy enough if the directory is already expanded, we can just select its
199  * children.  However if it is not then we must expand it and _when this has
200  * completed_ select its children.
201  *
202  * The way this is implented could cope with multiple directories but
203  * choose_selectchildren_sensitive() should stop this.
204  */
205 static void choose_selectchildren_activate
206     (GtkMenuItem attribute((unused)) *item,
207      gpointer attribute((unused)) userdata) {
208   gtk_tree_selection_selected_foreach(choose_selection,
209                                       choose_selectchildren_callback,
210                                       0);
211 }
212
213 /** @brief Pop-up menu for choose */
214 static struct menuitem choose_menuitems[] = {
215   {
216     "Play track",
217     choose_play_activate,
218     choose_play_sensitive,
219     0,
220     0
221   },
222   {
223     "Track properties",
224     choose_properties_activate,
225     choose_properties_sensitive,
226     0,
227     0
228   },
229   {
230     "Select children",
231     choose_selectchildren_activate,
232     choose_selectchildren_sensitive,
233     0,
234     0
235   },
236   {
237     "Deselect all tracks",
238     choose_selectnone_activate,
239     choose_selectnone_sensitive,
240     0,
241     0
242   },
243 };
244
245 const struct tabtype choose_tabtype = {
246   choose_properties_sensitive,
247   choose_selectall_sensitive,
248   choose_selectnone_sensitive,
249   choose_properties_activate,
250   choose_selectall_activate,
251   choose_selectnone_activate,
252   0,
253   0
254 };
255
256 /** @brief Called when a mouse button is pressed or released */
257 gboolean choose_button_event(GtkWidget attribute((unused)) *widget,
258                              GdkEventButton *event,
259                              gpointer attribute((unused)) user_data) {
260   if(event->type == GDK_BUTTON_RELEASE && event->button == 2) {
261     /* Middle click release - play track */
262     ensure_selected(GTK_TREE_VIEW(choose_view), event);
263     choose_play_activate(NULL, NULL);
264   } else if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
265     /* Right click press - pop up the menu */
266     ensure_selected(GTK_TREE_VIEW(choose_view), event);
267     popup(&choose_menu, event,
268           choose_menuitems, sizeof choose_menuitems / sizeof *choose_menuitems,
269           0);
270     return TRUE;
271   }
272   return FALSE;
273 }
274
275 /*
276 Local Variables:
277 c-basic-offset:2
278 comment-column:40
279 fill-column:79
280 indent-tabs-mode:nil
281 End:
282 */