1 /* Pcsx - Pc Psx Emulator
2 * Copyright (C) 1999-2002 Pcsx Team
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA
23 #include <gdk/gdkkeysyms.h>
25 #include <glade/glade.h>
30 #include "../libpcsxcore/sio.h"
32 #define MAX_MEMCARD_BLOCKS 15
35 static unsigned int currentIcon;
37 McdBlock Blocks[2][MAX_MEMCARD_BLOCKS]; // Assuming 2 cards, 15 blocks?
38 int IconC[2][MAX_MEMCARD_BLOCKS];
48 GtkWidget *GtkCList_McdList1, *GtkCList_McdList2;
50 static void AddColumns(GtkTreeView *treeview) {
51 GtkCellRenderer *renderer;
52 GtkTreeViewColumn *column;
55 renderer = gtk_cell_renderer_pixbuf_new ();
56 column = gtk_tree_view_column_new_with_attributes(_("Icon"),
57 renderer, "pixbuf", CL_ICON, NULL);
58 gtk_tree_view_append_column(treeview, column);
61 renderer = gtk_cell_renderer_text_new();
62 column = gtk_tree_view_column_new_with_attributes(_("Title"),
63 renderer, "text", CL_TITLE, NULL);
64 gtk_tree_view_append_column(treeview, column);
67 renderer = gtk_cell_renderer_text_new();
68 column = gtk_tree_view_column_new_with_attributes(_("Status"),
69 renderer, "text", CL_STAT, NULL);
70 gtk_tree_view_append_column(treeview, column);
73 renderer = gtk_cell_renderer_text_new();
74 column = gtk_tree_view_column_new_with_attributes(_("ID"),
75 renderer, "text", CL_ID, NULL);
76 gtk_tree_view_append_column(treeview, column);
79 renderer = gtk_cell_renderer_text_new();
80 column = gtk_tree_view_column_new_with_attributes(_("Name"),
81 renderer, "text", CL_NAME, NULL);
82 gtk_tree_view_append_column(treeview, column);
85 static GdkPixbuf *SetIcon(GtkWidget *dialog, short *icon, int i) {
93 visual = gdk_window_get_visual(dialog->window);
95 if (visual->depth == 8) return NULL;
97 image = gdk_image_new(GDK_IMAGE_NORMAL, visual, 32, 32);
99 for (y = 0; y < 32; y++) {
100 for (x = 0; x < 32; x++) {
101 c = icon[(y>>1) * 16 + (x>>1)];
102 c = ((c & 0x001f) << 10) | ((c & 0x7c00) >> 10) | (c & 0x03e0);
103 if (visual->depth == 16)
104 c = (c & 0x001f) | ((c & 0x7c00) << 1) | ((c & 0x03e0) << 1);
105 else if (visual->depth == 24 || visual->depth == 32)
106 c = ((c & 0x001f) << 3) | ((c & 0x03e0) << 6) | ((c & 0x7c00) << 9);
108 gdk_image_put_pixel(image, x, y, c);
112 pixmap = gdk_pixmap_new(dialog->window, 32, 32, visual->depth);
114 gc = gdk_gc_new(pixmap);
115 gdk_draw_image(pixmap, gc, image, 0, 0, 0, 0, 32, 32);
117 gdk_image_destroy(image);
119 pixbuf = gdk_pixbuf_get_from_drawable(NULL, GDK_PIXMAP (pixmap), NULL,
121 g_object_unref(pixmap);
126 static void LoadListItems(int mcd, GtkWidget *widget) {
136 store = gtk_list_store_new(NUM_CL, GDK_TYPE_PIXBUF, G_TYPE_STRING,
137 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
139 xml = glade_get_widget_tree(widget);
140 dialog = glade_xml_get_widget(xml, "McdsDlg");
142 if (mcd == 1) List = glade_xml_get_widget(xml, "GtkCList_McdList1");
143 else List = glade_xml_get_widget(xml, "GtkCList_McdList2");
145 for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) {
149 Info = &Blocks[mcd - 1][i];
150 IconC[mcd - 1][i] = 0;
152 if ((Info->Flags & 0xF0) == 0xA0) {
153 if ((Info->Flags & 0xF) >= 1 &&
154 (Info->Flags & 0xF) <= 3) {
155 state = _("Deleted");
158 } else if ((Info->Flags & 0xF0) == 0x50)
163 pixbuf = SetIcon(dialog, Info->Icon, i + 1);
165 gtk_list_store_append(store, &iter);
167 title = g_convert(Info->sTitle, strlen(Info->sTitle), "UTF-8",
168 "Shift-JIS", NULL, NULL, NULL);
170 gtk_list_store_set(store, &iter,
180 g_object_unref(pixbuf);
183 gtk_tree_view_set_model(GTK_TREE_VIEW(List), GTK_TREE_MODEL(store));
184 g_object_unref(G_OBJECT(store));
185 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(List), TRUE);
186 gtk_widget_show(List);
189 static void UpdateFilenameButtons(GtkWidget *widget) {
193 const char *filename;
196 xml = glade_get_widget_tree(widget);
197 dialog = glade_xml_get_widget(xml, "McdsDlg");
199 for (i = 0; i < 2; i++) {
201 widget = glade_xml_get_widget(xml, "Mcd1Label");
202 filename = Config.Mcd1;
204 widget = glade_xml_get_widget(xml, "Mcd2Label");
205 filename = Config.Mcd2;
208 p = g_path_get_basename(filename);
209 gtk_label_set_text(GTK_LABEL(widget), p);
214 static void LoadMcdDlg(GtkWidget *widget) {
217 for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) {
218 GetMcdBlockInfo(1, i + 1, &Blocks[0][i]);
219 GetMcdBlockInfo(2, i + 1, &Blocks[1][i]);
222 LoadListItems(1, widget);
223 LoadListItems(2, widget);
225 UpdateFilenameButtons(widget);
228 static void OnTreeSelectionChanged(GtkTreeSelection *selection, gpointer user_data);
230 static void UpdateListItems(int mcd, GtkWidget *widget) {
241 xml = glade_get_widget_tree(widget);
242 dialog = glade_xml_get_widget(xml, "McdsDlg");
244 if (mcd == 1) List = glade_xml_get_widget(xml, "GtkCList_McdList1");
245 else List = glade_xml_get_widget(xml, "GtkCList_McdList2");
247 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(List)));
248 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
250 for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) {
254 Info = &Blocks[mcd - 1][i];
255 IconC[mcd - 1][i] = 0;
257 if ((Info->Flags & 0xF0) == 0xA0) {
258 if ((Info->Flags & 0xF) >= 1 &&
259 (Info->Flags & 0xF) <= 3) {
260 state = _("Deleted");
263 } else if ((Info->Flags & 0xF0) == 0x50)
268 if (Info->IconCount > 0) {
269 pIcon = &Info->Icon[(currentIcon % Info->IconCount) * 16 * 16];
274 pixbuf = SetIcon(dialog, pIcon, i + 1);
275 title = g_convert(Info->sTitle, strlen(Info->sTitle), "UTF-8",
276 "Shift-JIS", NULL, NULL, NULL);
278 gtk_list_store_set(store, &iter,
288 g_object_unref(pixbuf);
289 gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
292 gtk_widget_show(List);
294 OnTreeSelectionChanged(gtk_tree_view_get_selection(GTK_TREE_VIEW(List)), (gpointer)mcd);
297 static void UpdateMcdDlg(GtkWidget *widget) {
300 for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) {
301 GetMcdBlockInfo(1, i + 1, &Blocks[0][i]);
302 GetMcdBlockInfo(2, i + 1, &Blocks[1][i]);
305 UpdateListItems(1, widget);
306 UpdateListItems(2, widget);
308 UpdateFilenameButtons(widget);
311 static void OnMcd_Close(GtkDialog *dialog, gint arg1, gpointer user_data) {
314 gtk_widget_destroy(GTK_WIDGET(dialog));
317 static void OnMcd_FileChange(GtkWidget *widget, gpointer user_data) {
318 gint memcard = (int)user_data;
322 // Ask for name of memory card
323 chooser = gtk_file_chooser_dialog_new(_("Select A File"),
324 NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
325 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
326 GTK_STOCK_OPEN, GTK_RESPONSE_OK,
330 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(chooser), Config.Mcd1);
332 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(chooser), Config.Mcd2);
334 if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) {
335 gtk_widget_hide(chooser);
337 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
339 if (filename != NULL) {
340 if (memcard == 1) strncpy(Config.Mcd1, filename, MAXPATHLEN);
341 else strncpy(Config.Mcd2, filename, MAXPATHLEN);
343 LoadMcd(memcard, filename);
350 gtk_widget_destroy(chooser);
353 // format a memory card
354 static void OnMcd_Format(GtkWidget *widget, gpointer user_data) {
356 GtkWidget *message_dialog;
360 gint memcard = (int)user_data;
362 message_dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
363 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
364 _("Format this Memory Card?"));
365 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(message_dialog),
366 _("If you format the memory card, the card will be empty, and any existing data overwritten."));
367 gtk_dialog_add_buttons(GTK_DIALOG(message_dialog),
368 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
369 _("Format card"), GTK_RESPONSE_YES, NULL);
371 result = gtk_dialog_run(GTK_DIALOG(message_dialog));
372 gtk_widget_destroy(message_dialog);
374 if (result == GTK_RESPONSE_YES) {
375 xml = glade_get_widget_tree(widget);
377 if (memcard == 1) str = Config.Mcd1;
378 else str = Config.Mcd2;
381 LoadMcd(memcard, str);
383 UpdateMcdDlg(widget);
387 // create a new, formatted memory card
388 static void OnMcd_New(GtkWidget *widget, gpointer user_data) {
392 // Ask for name of new memory card
393 chooser = gtk_file_chooser_dialog_new(_("Create a new Memory Card"),
394 NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
395 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
396 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
399 // Card should be put into $HOME/.pcsx/memcards
400 path = g_build_filename(g_get_home_dir(), ".pcsx", "memcards", NULL);
401 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), path);
402 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser), _("New Memory Card.mcd"));
403 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE);
405 if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) {
408 gtk_widget_hide(chooser);
409 name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
413 if ((int)user_data == 1) strncpy(Config.Mcd1, name, MAXPATHLEN);
414 else strncpy(Config.Mcd2, name, MAXPATHLEN);
416 LoadMcd((int)user_data, name);
422 gtk_widget_destroy(chooser);
426 static int copy = 0, copymcd = 0;
428 static int GetFreeMemcardSlot(int target_card) {
430 gboolean found = FALSE;
433 while (i < 15 && found == FALSE) {
434 Info = &Blocks[target_card][i];
435 if (g_ascii_strcasecmp(Info->Title, "") == 0) {
445 // no free slots, try to find a deleted one
447 while (i < 15 && found == FALSE) {
448 Info = &Blocks[target_card][i];
449 if ((Info->Flags & 0xF0) != 0x50) {
462 static void CopyMemcardData(char *from, char *to, gint *i, gchar *str) {
463 memcpy(to + (*i + 1) * 128, from + (copy + 1) * 128, 128);
464 SaveMcd((char *)str, to, (*i + 1) * 128, 128);
465 memcpy(to + (*i + 1) * 1024 * 8, from + (copy+1) * 1024 * 8, 1024 * 8);
466 SaveMcd((char *)str, to, (*i + 1) * 1024 * 8, 1024 * 8);
469 static void OnMcd_CopyTo(GtkWidget *widget, gpointer user_data) {
470 gint mcd = (gint)user_data;
477 GtkTreeSelection *treesel;
479 char *source, *destination;
483 xml = glade_get_widget_tree(widget);
486 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(GtkCList_McdList2));
488 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(GtkCList_McdList1));
490 // If the item selected is not reported as a 'Free' slot
491 if (gtk_tree_selection_get_selected(treesel, &model, &iter)) {
492 path = gtk_tree_model_get_path(model, &iter);
493 i = gtk_tree_path_get_indices(path);
496 gtk_tree_path_free(path);
499 // Determine the first free slot in the target memory card
500 first_free_slot = GetFreeMemcardSlot(mcd - 1);
501 if (first_free_slot == -1) {
502 // No free slots available on the destination card
503 SysErrorMessage(_("No free space on memory card"),
504 _("There are no free slots available on the target memory card. Please delete a slot first."));
508 xml = glade_get_widget_tree(GtkCList_McdList1);
513 destination = Mcd1Data;
517 destination = Mcd2Data;
520 CopyMemcardData(source, destination, &first_free_slot, str);
521 UpdateMcdDlg(widget);
524 static void OnMemcardDelete(GtkWidget *widget, gpointer user_data) {
535 GtkTreeSelection *sel;
537 gint memcard = (int)user_data;
539 xml = glade_get_widget_tree(widget);
542 tree = glade_xml_get_widget(xml, "GtkCList_McdList1");
543 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (tree));
544 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
546 filename = Config.Mcd1;
548 tree = glade_xml_get_widget(xml, "GtkCList_McdList2");
549 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (tree));
550 selected = gtk_tree_selection_get_selected(sel, &model, &iter);
552 filename = Config.Mcd2;
556 path = gtk_tree_model_get_path(model, &iter);
557 i = *gtk_tree_path_get_indices(path);
560 ptr = data + i * 128;
561 Info = &Blocks[memcard - 1][i - 1];
563 if ((Info->Flags & 0xF0) == 0xA0) {
564 if ((Info->Flags & 0xF) >= 1 &&
565 (Info->Flags & 0xF) <= 3) { // deleted
566 *ptr = 0x50 | (Info->Flags & 0xF);
568 } else if ((Info->Flags & 0xF0) == 0x50) { // used
569 *ptr = 0xA0 | (Info->Flags & 0xF);
572 for (j = 0; j < 127; j++) xor ^= *ptr++;
575 SaveMcd((char *)filename, data, i * 128, 128);
576 UpdateMcdDlg(widget);
580 static void OnTreeSelectionChanged(GtkTreeSelection *selection, gpointer user_data) {
590 gint memcard = (int)user_data;
592 xml = glade_get_widget_tree(GtkCList_McdList1);
593 selected = gtk_tree_selection_get_selected(selection, &model, &iter);
596 path = gtk_tree_model_get_path(model, &iter);
597 i = *gtk_tree_path_get_indices(path);
598 gtk_tree_path_free(path);
600 // If a row was selected, and the row is not blank, we can now enable
601 // some of the disabled widgets
603 GetMcdBlockInfo(1, i + 1, &b);
605 if ((b.Flags >= 0xA1 && b.Flags <= 0xA3) || ((b.Flags & 0xF0) == 0x50)) {
606 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_Delete1"), TRUE);
608 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_Delete1"), FALSE);
611 if ((b.Flags & 0xF0) == 0x50) {
612 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_CopyTo2"), TRUE);
614 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_CopyTo2"), FALSE);
617 GetMcdBlockInfo(2, i + 1, &b);
619 if ((b.Flags >= 0xA1 && b.Flags <= 0xA3) || ((b.Flags & 0xF0) == 0x50)) {
620 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_Delete2"), TRUE);
622 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_Delete2"), FALSE);
625 if ((b.Flags & 0xF0) == 0x50) {
626 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_CopyTo1"), TRUE);
628 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_CopyTo1"), FALSE);
633 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_CopyTo2"), FALSE);
634 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_Delete1"), FALSE);
636 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_CopyTo1"), FALSE);
637 gtk_widget_set_sensitive(glade_xml_get_widget(xml, "GtkButton_Delete2"), FALSE);
642 gboolean updateFunc(gpointer data) {
643 if (quit) return FALSE;
645 UpdateListItems(1, GtkCList_McdList1);
646 UpdateListItems(2, GtkCList_McdList2);
647 g_timeout_add(333, updateFunc, 0);
655 GtkTreeSelection *treesel1, *treesel2;
658 xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "McdsDlg", NULL);
661 g_warning("We could not load the interface!");
665 dialog = glade_xml_get_widget(xml, "McdsDlg");
667 gtk_window_set_title(GTK_WINDOW(dialog), _("Memory Card Manager"));
669 // Assign default memory cards
670 if (!strlen(Config.Mcd1)) {
671 str = g_strconcat(getenv("HOME"), DEFAULT_MEM_CARD_1, NULL);
672 strcpy(Config.Mcd1, str);
676 if (!strlen(Config.Mcd2)) {
677 str = g_strconcat(getenv("HOME"), DEFAULT_MEM_CARD_2, NULL);
678 strcpy(Config.Mcd2, str);
682 GtkCList_McdList1 = glade_xml_get_widget(xml, "GtkCList_McdList1");
683 AddColumns(GTK_TREE_VIEW(GtkCList_McdList1));
684 GtkCList_McdList2 = glade_xml_get_widget(xml, "GtkCList_McdList2");
685 AddColumns(GTK_TREE_VIEW(GtkCList_McdList2));
687 treesel1 = gtk_tree_view_get_selection(GTK_TREE_VIEW (GtkCList_McdList1));
688 gtk_tree_selection_set_mode(treesel1, GTK_SELECTION_SINGLE);
689 g_signal_connect_data(G_OBJECT(treesel1), "changed",
690 G_CALLBACK(OnTreeSelectionChanged),
691 (gpointer)1, NULL, G_CONNECT_AFTER);
693 treesel2 = gtk_tree_view_get_selection(GTK_TREE_VIEW (GtkCList_McdList2));
694 gtk_tree_selection_set_mode(treesel2, GTK_SELECTION_SINGLE);
695 g_signal_connect_data(G_OBJECT(treesel2), "changed",
696 G_CALLBACK(OnTreeSelectionChanged),
697 (gpointer)2, NULL, G_CONNECT_AFTER);
701 // Setup a handler for when Close or Cancel is clicked
702 g_signal_connect_data(GTK_OBJECT(dialog), "response",
703 GTK_SIGNAL_FUNC(OnMcd_Close), xml, (GClosureNotify)g_object_unref, G_CONNECT_AFTER);
705 widget = glade_xml_get_widget(xml, "GtkButton_Format1");
706 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
707 GTK_SIGNAL_FUNC(OnMcd_Format), (gpointer)1, NULL, G_CONNECT_AFTER);
709 widget = glade_xml_get_widget(xml, "GtkButton_Format2");
710 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
711 GTK_SIGNAL_FUNC(OnMcd_Format), (gpointer)2, NULL, G_CONNECT_AFTER);
713 widget = glade_xml_get_widget(xml, "Mcd1Button");
714 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
715 GTK_SIGNAL_FUNC(OnMcd_FileChange), (gpointer)1, NULL, G_CONNECT_AFTER);
717 widget = glade_xml_get_widget(xml, "Mcd2Button");
718 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
719 GTK_SIGNAL_FUNC(OnMcd_FileChange), (gpointer)2, NULL, G_CONNECT_AFTER);
721 widget = glade_xml_get_widget(xml, "GtkButton_New1");
722 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
723 GTK_SIGNAL_FUNC(OnMcd_New), (gpointer)1, NULL, G_CONNECT_AFTER);
725 widget = glade_xml_get_widget(xml, "GtkButton_New2");
726 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
727 GTK_SIGNAL_FUNC(OnMcd_New), (gpointer)2, NULL, G_CONNECT_AFTER);
729 widget = glade_xml_get_widget(xml, "GtkButton_CopyTo1");
730 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
731 GTK_SIGNAL_FUNC(OnMcd_CopyTo), (gpointer)1, NULL, G_CONNECT_AFTER);
732 gtk_widget_set_sensitive(GTK_WIDGET(widget), FALSE);
734 widget = glade_xml_get_widget(xml, "GtkButton_CopyTo2");
735 g_signal_connect_data(GTK_OBJECT(widget), "clicked",
736 GTK_SIGNAL_FUNC(OnMcd_CopyTo), (gpointer)2, NULL, G_CONNECT_AFTER);
737 gtk_widget_set_sensitive(GTK_WIDGET(widget), FALSE);
739 widget = glade_xml_get_widget(xml, "GtkButton_Delete1");
740 g_signal_connect_data (GTK_OBJECT (widget), "clicked",
741 GTK_SIGNAL_FUNC(OnMemcardDelete), (gpointer)1, NULL, G_CONNECT_AFTER);
742 gtk_widget_set_sensitive(GTK_WIDGET(widget), FALSE);
744 widget = glade_xml_get_widget(xml, "GtkButton_Delete2");
745 g_signal_connect_data (GTK_OBJECT (widget), "clicked",
746 GTK_SIGNAL_FUNC(OnMemcardDelete), (gpointer)2, NULL, G_CONNECT_AFTER);
747 gtk_widget_set_sensitive(GTK_WIDGET(widget), FALSE);
752 g_timeout_add(1, updateFunc, 0);
754 while (gtk_events_pending()) { gtk_main_iteration(); }