1 /* browse.c: tape browser dialog box
2 Copyright (c) 2002-2004 Philip Kendall
3
4 $Id: browse.c 4708 2012-05-25 12:14:50Z fredm $
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 Author contact information:
21
22 E-mail: philip-fuse@shadowmagic.org.uk
23
24 */
25
26 #include <config.h>
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <gdk/gdkkeysyms.h>
32 #include <gtk/gtk.h>
33
34 #include "compat.h"
35 #include "fuse.h"
36 #include "gtkinternals.h"
37 #include "menu.h"
38 #include "tape.h"
39 #include "ui/ui.h"
40
41 static int create_dialog( void );
42 static void add_block_details( libspectrum_tape_block *block,
43 void *user_data );
44 static void select_row( GtkTreeView *treeview, GtkTreePath *path,
45 GtkTreeViewColumn *col, gpointer user_data );
46 void mark_row( GtkTreeModel *model, int row );
47 static void browse_done( GtkWidget *widget, gpointer data );
48 static gboolean delete_dialog( GtkWidget *widget, GdkEvent *event,
49 gpointer user_data );
50
51 static GdkPixbuf *tape_marker_pixbuf;
52
53 static GtkWidget
54 *dialog, /* The dialog box itself */
55 *blocks, /* The list of blocks */
56 *modified_label; /* The label saying if the tape has been
57 modified */
58
59 static int dialog_created; /* Have we created the dialog box yet? */
60
61 /* List columns */
62 enum
63 {
64 COL_PIX = 0, /* Pixmap */
65 COL_BLOCK, /* Block type */
66 COL_DATA, /* Data detail */
67 NUM_COLS
68 };
69
70 void
menu_media_tape_browse(GtkAction * gtk_action GCC_UNUSED,gpointer data GCC_UNUSED)71 menu_media_tape_browse( GtkAction *gtk_action GCC_UNUSED,
72 gpointer data GCC_UNUSED )
73 {
74 /* Firstly, stop emulation */
75 fuse_emulation_pause();
76
77 if( !dialog_created )
78 if( create_dialog() ) { fuse_emulation_unpause(); return; }
79
80 if( ui_tape_browser_update( UI_TAPE_BROWSER_NEW_TAPE, NULL ) ) {
81 fuse_emulation_unpause();
82 return;
83 }
84
85 gtk_widget_show_all( dialog );
86
87 /* Carry on with emulation */
88 fuse_emulation_unpause();
89 }
90
91 GtkWidget *
create_block_list(void)92 create_block_list( void )
93 {
94 GtkWidget *view;
95 GtkCellRenderer *renderer;
96 GtkTreeModel *model;
97 GtkListStore *store;
98
99 view = gtk_tree_view_new();
100
101 /* Add columns */
102 renderer = gtk_cell_renderer_pixbuf_new();
103 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
104 -1,
105 NULL,
106 renderer,
107 "pixbuf", COL_PIX,
108 NULL );
109
110 renderer = gtk_cell_renderer_text_new();
111 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
112 -1,
113 "Block type",
114 renderer,
115 "text", COL_BLOCK,
116 NULL );
117
118 renderer = gtk_cell_renderer_text_new();
119 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
120 -1,
121 "Data",
122 renderer,
123 "text", COL_DATA,
124 NULL );
125
126 /* Create data model */
127 store = gtk_list_store_new( NUM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
128 G_TYPE_STRING );
129
130 model = GTK_TREE_MODEL( store );
131 gtk_tree_view_set_model( GTK_TREE_VIEW( view ), model );
132 g_object_unref( model );
133
134 /* Fast move tape */
135 g_signal_connect( G_OBJECT( view ), "row-activated",
136 G_CALLBACK( select_row ), model );
137
138 return view;
139 }
140
141 static int
create_dialog(void)142 create_dialog( void )
143 {
144 GtkWidget *scrolled_window, *content_area;
145
146 /* Give me a new dialog box */
147 dialog = gtkstock_dialog_new( "Fuse - Browse Tape",
148 G_CALLBACK( delete_dialog ) );
149 content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
150
151 /* And a scrolled window to pack the list into */
152 scrolled_window = gtk_scrolled_window_new( NULL, NULL );
153 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_window ),
154 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
155 gtk_box_pack_start( GTK_BOX( content_area ), scrolled_window, TRUE, TRUE, 0 );
156
157 /* The tape marker pixbuf */
158 tape_marker_pixbuf = gdk_pixbuf_new_from_xpm_data( gtkpixmap_tape_marker );
159 /* FIXME: unref this at exit */
160
161 /* And the list itself */
162 blocks = create_block_list();
163 gtk_container_add( GTK_CONTAINER( scrolled_window ), GTK_WIDGET( blocks ) );
164
165 /* And the "tape modified" label */
166 modified_label = gtk_label_new( "" );
167 gtk_box_pack_start( GTK_BOX( content_area ), modified_label, FALSE, FALSE, 0 );
168
169 /* Create the OK button */
170 gtkstock_create_close( dialog, NULL, G_CALLBACK( browse_done ), FALSE );
171
172 /* Make the window big enough to show at least some data */
173 gtk_window_set_default_size( GTK_WINDOW( dialog ), -1, 250 );
174
175 dialog_created = 1;
176
177 return 0;
178 }
179
180 int
ui_tape_browser_update(ui_tape_browser_update_type change GCC_UNUSED,libspectrum_tape_block * block GCC_UNUSED)181 ui_tape_browser_update( ui_tape_browser_update_type change GCC_UNUSED,
182 libspectrum_tape_block *block GCC_UNUSED )
183 {
184 int error, current_block;
185 GtkTreeModel *model;
186
187 if( !dialog_created ) return 0;
188
189 fuse_emulation_pause();
190
191 model = gtk_tree_view_get_model( GTK_TREE_VIEW( blocks ) );
192 gtk_list_store_clear( GTK_LIST_STORE( model ) );
193
194 error = tape_foreach( add_block_details, model );
195 if( error ) {
196 fuse_emulation_unpause();
197 return 1;
198 }
199
200 current_block = tape_get_current_block();
201
202 if( current_block != -1 )
203 mark_row( model, current_block );
204
205 if( tape_modified ) {
206 gtk_label_set_text( GTK_LABEL( modified_label ), "Tape modified" );
207 } else {
208 gtk_label_set_text( GTK_LABEL( modified_label ), "Tape not modified" );
209 }
210
211 fuse_emulation_unpause();
212
213 return 0;
214 }
215
216 static void
add_block_details(libspectrum_tape_block * block,void * user_data)217 add_block_details( libspectrum_tape_block *block, void *user_data )
218 {
219 gchar block_type[80];
220 gchar data_detail[80];
221 GtkTreeIter iter;
222 GtkTreeModel *model = user_data;
223
224 libspectrum_tape_block_description( block_type, 80, block );
225 tape_block_details( data_detail, 80, block );
226
227 /* Append a new row and fill data */
228 gtk_list_store_append( GTK_LIST_STORE( model ), &iter );
229 gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
230 COL_PIX, NULL,
231 COL_BLOCK, block_type,
232 COL_DATA, data_detail,
233 -1 );
234 }
235
236 /* Called when a row is selected */
237 static void
select_row(GtkTreeView * treeview GCC_UNUSED,GtkTreePath * path,GtkTreeViewColumn * col GCC_UNUSED,gpointer user_data)238 select_row( GtkTreeView *treeview GCC_UNUSED, GtkTreePath *path,
239 GtkTreeViewColumn *col GCC_UNUSED, gpointer user_data )
240 {
241 int current_block;
242 int *indices;
243 int row;
244 GtkTreeIter iter;
245 GtkTreeModel *model = user_data;
246
247 /* Get selected row */
248 row = -1;
249 indices = gtk_tree_path_get_indices( path );
250 if( indices ) row = indices[0];
251
252 /* Don't do anything if the current block was clicked on */
253 current_block = tape_get_current_block();
254 if( row == current_block ) return;
255
256 /* Otherwise, select the new block */
257 tape_select_block_no_update( row );
258
259 /* Mark selected block */
260 if( current_block != -1 ) {
261 gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &iter, path );
262
263 gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
264 COL_PIX, tape_marker_pixbuf,
265 -1 );
266 }
267
268 /* Unmark former block */
269 if( current_block != -1 ) {
270 path = gtk_tree_path_new_from_indices( current_block, -1 );
271
272 if( gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &iter, path ) ) {
273 gtk_tree_path_free( path );
274 return;
275 }
276
277 gtk_tree_path_free( path );
278
279 gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
280 COL_PIX, NULL,
281 -1 );
282 }
283 }
284
285 void
mark_row(GtkTreeModel * model,int row)286 mark_row( GtkTreeModel *model, int row )
287 {
288 GtkTreeIter iter;
289 GtkTreePath *path;
290
291 path = gtk_tree_path_new_from_indices( row, -1 );
292
293 if( !gtk_tree_model_get_iter( model, &iter, path ) ) {
294 gtk_tree_path_free( path );
295 return;
296 }
297
298 gtk_tree_path_free( path );
299
300 gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
301 COL_PIX, tape_marker_pixbuf,
302 -1 );
303 }
304
305 /* Called if the OK button is clicked */
306 static void
browse_done(GtkWidget * widget GCC_UNUSED,gpointer data GCC_UNUSED)307 browse_done( GtkWidget *widget GCC_UNUSED, gpointer data GCC_UNUSED )
308 {
309 gtk_widget_hide( dialog );
310 }
311
312 /* Catch attempts to delete the window and just hide it instead */
313 static gboolean
delete_dialog(GtkWidget * widget,GdkEvent * event GCC_UNUSED,gpointer user_data GCC_UNUSED)314 delete_dialog( GtkWidget *widget, GdkEvent *event GCC_UNUSED,
315 gpointer user_data GCC_UNUSED )
316 {
317 gtk_widget_hide( widget );
318 return TRUE;
319 }
320