[file-roller] use the eggfileformatchooser widget to select the file format



commit 6e8f95a6a8290a5aa1d049453a6a580a131ad6bf
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Nov 29 17:47:11 2009 +0100

    use the eggfileformatchooser widget to select the file format

 data/ui/new.ui             |  355 ++++++-------
 src/Makefile.am            |    3 +
 src/actions.c              |    4 +-
 src/dlg-new.c              |  146 +++++-
 src/dlg-new.h              |    4 +-
 src/egg-macros.h           |  154 ++++++
 src/eggfileformatchooser.c | 1219 ++++++++++++++++++++++++++++++++++++++++++++
 src/eggfileformatchooser.h |   85 +++
 src/main.c                 |   14 +-
 9 files changed, 1772 insertions(+), 212 deletions(-)
---
diff --git a/data/ui/new.ui b/data/ui/new.ui
index fb9ffee..70c0742 100644
--- a/data/ui/new.ui
+++ b/data/ui/new.ui
@@ -7,199 +7,16 @@
     <property name="modal">True</property>
     <property name="window_position">center-on-parent</property>
     <property name="type_hint">normal</property>
+    <property name="extra_widget">extra_widget</property>
     <property name="action">save</property>
     <property name="do_overwrite_confirmation">True</property>
     <child internal-child="vbox">
       <object class="GtkVBox" id="dialog-vbox1">
         <property name="visible">True</property>
+        <property name="orientation">vertical</property>
         <property name="spacing">2</property>
         <child>
-          <object class="GtkVBox" id="vbox1">
-            <property name="visible">True</property>
-            <property name="border_width">6</property>
-            <property name="spacing">12</property>
-            <child>
-              <object class="GtkHBox" id="hbox1">
-                <property name="visible">True</property>
-                <property name="spacing">12</property>
-                <child>
-                  <object class="GtkLabel" id="n_archive_type_label">
-                    <property name="visible">True</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Archive _type:</property>
-                    <property name="use_markup">True</property>
-                    <property name="use_underline">True</property>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkHBox" id="n_archive_type_box">
-                    <property name="visible">True</property>
-                    <property name="spacing">6</property>
-                    <child>
-                      <placeholder/>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkExpander" id="n_other_options_expander">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="expanded">True</property>
-                <child>
-                  <object class="GtkAlignment" id="alignment1">
-                    <property name="visible">True</property>
-                    <property name="top_padding">6</property>
-                    <child>
-                      <object class="GtkVBox" id="vbox2">
-                        <property name="visible">True</property>
-                        <property name="spacing">6</property>
-                        <child>
-                          <object class="GtkHBox" id="hbox2">
-                            <property name="visible">True</property>
-                            <child>
-                              <object class="GtkTable" id="table1">
-                                <property name="visible">True</property>
-                                <property name="n_rows">3</property>
-                                <property name="n_columns">2</property>
-                                <property name="column_spacing">12</property>
-                                <property name="row_spacing">6</property>
-                                <child>
-                                  <object class="GtkLabel" id="n_password_label">
-                                    <property name="visible">True</property>
-                                    <property name="xalign">0</property>
-                                    <property name="label" translatable="yes">_Password:</property>
-                                    <property name="use_underline">True</property>
-                                    <property name="mnemonic_widget">n_password_entry</property>
-                                  </object>
-                                  <packing>
-                                    <property name="bottom_attach">2</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkEntry" id="n_password_entry">
-                                    <property name="width_request">300</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="visibility">False</property>
-                                    <property name="invisible_char">&#x25CF;</property>
-                                  </object>
-                                  <packing>
-                                    <property name="left_attach">1</property>
-                                    <property name="right_attach">2</property>
-                                    <property name="bottom_attach">2</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkCheckButton" id="n_encrypt_header_checkbutton">
-                                    <property name="label" translatable="yes">_Encrypt the file list too</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="use_underline">True</property>
-                                    <property name="draw_indicator">True</property>
-                                  </object>
-                                  <packing>
-                                    <property name="left_attach">1</property>
-                                    <property name="right_attach">2</property>
-                                    <property name="top_attach">2</property>
-                                    <property name="bottom_attach">3</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                          </object>
-                          <packing>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkHBox" id="n_volume_box">
-                            <property name="visible">True</property>
-                            <property name="spacing">6</property>
-                            <child>
-                              <object class="GtkCheckButton" id="n_volume_checkbutton">
-                                <property name="label" translatable="yes">Split in _volumes of</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="receives_default">False</property>
-                                <property name="use_underline">True</property>
-                                <property name="draw_indicator">True</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkSpinButton" id="n_volume_spinbutton">
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="adjustment">volume_adjustment</property>
-                                <property name="climb_rate">1</property>
-                                <property name="digits">1</property>
-                                <property name="numeric">True</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="label4">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes" comments="MB means megabytes">MB</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="position">2</property>
-                              </packing>
-                            </child>
-                          </object>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child type="label">
-                  <object class="GtkLabel" id="label2">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">_Other Options</property>
-                    <property name="use_underline">True</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="position">2</property>
-          </packing>
+          <placeholder/>
         </child>
         <child internal-child="action_area">
           <object class="GtkHButtonBox" id="dialog-action_area1">
@@ -246,6 +63,7 @@
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="position">2</property>
+                <property name="secondary">True</property>
               </packing>
             </child>
           </object>
@@ -264,10 +82,173 @@
     </action-widgets>
   </object>
   <object class="GtkAdjustment" id="volume_adjustment">
-    <property name="value">10</property>
     <property name="lower">0.10000000149</property>
     <property name="upper">1000</property>
+    <property name="value">10</property>
     <property name="step_increment">0.10000000149</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkVBox" id="extra_widget">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">12</property>
+    <child>
+      <object class="GtkVBox" id="format_chooser_box">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+      <packing>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkExpander" id="n_other_options_expander">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="expanded">True</property>
+        <child>
+          <object class="GtkAlignment" id="other_oprtions_alignment">
+            <property name="visible">True</property>
+            <property name="top_padding">6</property>
+            <child>
+              <object class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkTable" id="table1">
+                        <property name="visible">True</property>
+                        <property name="n_rows">3</property>
+                        <property name="n_columns">2</property>
+                        <property name="column_spacing">12</property>
+                        <property name="row_spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="n_password_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">_Password:</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">n_password_entry</property>
+                          </object>
+                          <packing>
+                            <property name="bottom_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="n_password_entry">
+                            <property name="width_request">300</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="visibility">False</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="bottom_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="n_encrypt_header_checkbutton">
+                            <property name="label" translatable="yes">_Encrypt the file list too</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="n_volume_box">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkCheckButton" id="n_volume_checkbutton">
+                        <property name="label" translatable="yes">Split in _volumes of</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="n_volume_spinbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x25CF;</property>
+                        <property name="adjustment">volume_adjustment</property>
+                        <property name="climb_rate">1</property>
+                        <property name="digits">1</property>
+                        <property name="numeric">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label4">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes" comments="MB means megabytes">MB</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="label2">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">_Other Options</property>
+            <property name="use_underline">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
 </interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index 2eedf46..6c8e767 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,6 +75,9 @@ file_roller_SOURCES = 			\
 	dlg-prop.h			\
 	dlg-update.c			\
 	dlg-update.h			\
+	eggfileformatchooser.c		\
+	eggfileformatchooser.h		\
+	egg-macros.h			\
 	eggtreemultidnd.c		\
 	eggtreemultidnd.h		\
 	file-data.c			\
diff --git a/src/actions.c b/src/actions.c
index a7ef8ff..1f7db4c 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -119,7 +119,7 @@ get_full_uri (DlgNewData *data)
 		return NULL;
 	}
 
-	idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box));
+	idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
 	if (idx > 0) {
 		const char *uri_ext;
 		char       *default_ext;
@@ -211,7 +211,7 @@ get_archive_filename_from_selector (DlgNewData *data)
 		char *new_uri;
 		char *ext = NULL;
 
-		idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box));
+		idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
 		if (idx > 0)
 			ext = mime_type_desc[data->supported_types[idx-1]].default_ext;
 		else
diff --git a/src/dlg-new.c b/src/dlg-new.c
index 9fe3288..1cfc1ca 100644
--- a/src/dlg-new.c
+++ b/src/dlg-new.c
@@ -34,6 +34,7 @@
 #include "preferences.h"
 
 
+#define GET_WIDGET(x) (_gtk_builder_get_widget (data->builder, (x)))
 #define DEFAULT_EXTENSION ".tar.gz"
 #define BAD_CHARS "/\\*"
 #define MEGABYTE (1024 * 1024)
@@ -117,7 +118,8 @@ get_archive_type (DlgNewData *data)
 	if (ext == NULL) {
 		int idx;
 
-		idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box)) - 1;
+		idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
+		/*idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box)) - 1;*/
 		if (idx >= 0)
 			return data->supported_types[idx];
 
@@ -128,6 +130,7 @@ get_archive_type (DlgNewData *data)
 }
 
 
+/* FIXME
 static void
 archive_type_combo_box_changed_cb (GtkComboBox *combo_box,
 				   DlgNewData  *data)
@@ -169,6 +172,7 @@ archive_type_combo_box_changed_cb (GtkComboBox *combo_box,
 		g_free (basename_noext);
 	}
 }
+*/
 
 
 static void
@@ -187,15 +191,89 @@ volume_toggled_cb (GtkToggleButton *toggle_button,
 }
 
 
+static void
+format_chooser_selection_changed_cb (EggFileFormatChooser *format_chooser,
+				     DlgNewData           *data)
+{
+	const char *uri;
+	const char *ext;
+	int         n_format;
+
+	uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog));
+	if (uri == NULL)
+		return;
+
+	ext = get_archive_filename_extension (uri);
+	n_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
+	if (ext == NULL)
+		ext = mime_type_desc[data->supported_types[n_format - 1]].default_ext;
+
+	update_sensitivity_for_ext (data, ext);
+
+	if (uri != NULL) {
+		const char *new_ext;
+		const char *basename;
+		char       *basename_noext;
+		char       *new_basename;
+		char       *new_basename_uft8;
+
+		new_ext = mime_type_desc[data->supported_types[n_format - 1]].default_ext;
+		basename = file_name_from_path (uri);
+		if (g_str_has_suffix (basename, ext))
+			basename_noext = g_strndup (basename, strlen (basename) - strlen (ext));
+		else
+			basename_noext = g_strdup (basename);
+		new_basename = g_strconcat (basename_noext, new_ext, NULL);
+		new_basename_uft8 = g_uri_unescape_string (new_basename, NULL);
+
+		gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), new_basename_uft8);
+		update_sensitivity_for_ext (data, new_ext);
+
+		g_free (new_basename_uft8);
+		g_free (new_basename);
+		g_free (basename_noext);
+	}
+}
+
+
+static char *
+get_icon_name_for_type (const char *mime_type)
+{
+	char *name = NULL;
+
+	if (mime_type != NULL) {
+		char *s;
+
+		name = g_strconcat ("gnome-mime-", mime_type, NULL);
+		for (s = name; *s; ++s)
+			if (! g_ascii_isalpha (*s))
+				*s = '-';
+	}
+
+	if ((name == NULL) || ! gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name)) {
+		g_free (name);
+		name = g_strdup ("package-x-generic");
+	}
+
+	return name;
+}
+
+
+static void
+options_expander_unmap_cb (GtkWidget *widget,
+			   gpointer   user_data)
+{
+	egg_file_format_chooser_emit_size_changed ((EggFileFormatChooser *) user_data);
+}
+
+
 static DlgNewData *
 dlg_new_archive (FrWindow  *window,
 		int        *supported_types,
 		const char *default_name)
 {
 	DlgNewData    *data;
-	GtkWidget     *n_archive_type_box;
 	GtkWidget     *n_new_button;
-	GtkSizeGroup  *size_group;
 	GtkFileFilter *filter;
 	/*char          *default_ext;*/
 	int            i;
@@ -225,7 +303,6 @@ dlg_new_archive (FrWindow  *window,
 	data->n_volume_spinbutton = _gtk_builder_get_widget (data->builder, "n_volume_spinbutton");
 	data->n_volume_box = _gtk_builder_get_widget (data->builder, "n_volume_box");
 
-	n_archive_type_box = _gtk_builder_get_widget (data->builder, "n_archive_type_box");
 	n_new_button = _gtk_builder_get_widget (data->builder, "n_new_button");
 
 	/* Set widgets data. */
@@ -258,28 +335,51 @@ dlg_new_archive (FrWindow  *window,
 
 	/**/
 
-	size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-	gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "n_archive_type_label"));
-	gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "n_password_label"));
-
 	gtk_button_set_use_stock (GTK_BUTTON (n_new_button), TRUE);
 	gtk_button_set_label (GTK_BUTTON (n_new_button), FR_STOCK_CREATE_ARCHIVE);
 	gtk_expander_set_expanded (GTK_EXPANDER (data->n_other_options_expander), FALSE);
 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->n_encrypt_header_checkbutton), eel_gconf_get_boolean (PREF_ENCRYPT_HEADER, FALSE));
 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->n_volume_spinbutton), (double) eel_gconf_get_integer (PREF_BATCH_VOLUME_SIZE, 0) / MEGABYTE);
 
-	/* archive type combobox */
+	/* format chooser */
 
-	data->n_archive_type_combo_box = gtk_combo_box_new_text ();
-	gtk_combo_box_append_text (GTK_COMBO_BOX (data->n_archive_type_combo_box), _("Automatic"));
+	data->format_chooser = (EggFileFormatChooser *) egg_file_format_chooser_new ();
 	for (i = 0; data->supported_types[i] != -1; i++) {
-		int idx = data->supported_types[i];
-		gtk_combo_box_append_text (GTK_COMBO_BOX (data->n_archive_type_combo_box),
-					   _(mime_type_desc[idx].name));
+		int   idx = data->supported_types[i];
+		char *exts[4];
+		int   e;
+		int   n_exts;
+		char *icon_name;
+
+		n_exts = 0;
+		for (e = 0; (n_exts < 4) && file_ext_type[e].ext != NULL; e++) {
+			if (strcmp (file_ext_type[e].ext, mime_type_desc[idx].default_ext) == 0)
+				continue;
+			if (strcmp (file_ext_type[e].mime_type, mime_type_desc[idx].mime_type) == 0)
+				exts[n_exts++] = file_ext_type[e].ext;
+		}
+		while (n_exts < 4)
+			exts[n_exts++] = NULL;
+
+		g_print ("%s => %s, %s, %s, %s\n", mime_type_desc[idx].mime_type, exts[0], exts[1], exts[2], exts[3]);
+
+		icon_name = get_icon_name_for_type (mime_type_desc[idx].mime_type);
+		egg_file_format_chooser_add_format (data->format_chooser,
+						    0,
+						    _(mime_type_desc[idx].name),
+						    icon_name,
+						    mime_type_desc[idx].default_ext,
+						    exts[0],
+						    exts[1],
+						    exts[2],
+						    exts[3],
+						    NULL);
+
+		g_free (icon_name);
 	}
-	gtk_combo_box_set_active (GTK_COMBO_BOX (data->n_archive_type_combo_box), 0);
-	gtk_box_pack_start (GTK_BOX (n_archive_type_box), data->n_archive_type_combo_box, TRUE, TRUE, 0);
-	gtk_widget_show_all (n_archive_type_box);
+	egg_file_format_chooser_set_format (data->format_chooser, 0);
+	gtk_widget_show (GTK_WIDGET (data->format_chooser));
+	gtk_box_pack_start (GTK_BOX (GET_WIDGET ("format_chooser_box")), GTK_WIDGET (data->format_chooser), TRUE, TRUE, 0);
 
 	/* Set the signals handlers. */
 
@@ -303,10 +403,10 @@ dlg_new_archive (FrWindow  *window,
 			  G_CALLBACK (add_clicked_cb),
 			  data);*/
 
-	g_signal_connect (G_OBJECT (data->n_archive_type_combo_box),
+	/* FIXME g_signal_connect (G_OBJECT (data->n_archive_type_combo_box),
 			  "changed",
 			  G_CALLBACK (archive_type_combo_box_changed_cb),
-			  data);
+			  data); */
 	g_signal_connect (G_OBJECT (data->n_password_entry),
 			  "changed",
 			  G_CALLBACK (password_entry_changed_cb),
@@ -315,6 +415,14 @@ dlg_new_archive (FrWindow  *window,
 			  "toggled",
 			  G_CALLBACK (volume_toggled_cb),
 			  data);
+	g_signal_connect (G_OBJECT (data->format_chooser),
+			  "selection-changed",
+			  G_CALLBACK (format_chooser_selection_changed_cb),
+			  data);
+	g_signal_connect_after (GET_WIDGET ("other_oprtions_alignment"),
+				"unmap",
+				G_CALLBACK (options_expander_unmap_cb),
+				data->format_chooser);
 
 	/* Run dialog. */
 
diff --git a/src/dlg-new.h b/src/dlg-new.h
index 8c9328b..58bc86d 100644
--- a/src/dlg-new.h
+++ b/src/dlg-new.h
@@ -24,6 +24,7 @@
 #define DLG_NEW_H
 
 #include <gtk/gtk.h>
+#include "eggfileformatchooser.h"
 #include "fr-window.h"
 
 
@@ -36,7 +37,7 @@ typedef struct {
 	GtkBuilder *builder;
 
 	GtkWidget  *dialog;
-	GtkWidget  *n_archive_type_combo_box;
+	/*GtkWidget  *n_archive_type_combo_box;*/
 	GtkWidget  *n_other_options_expander;
 	GtkWidget  *n_password_entry;
 	GtkWidget  *n_password_label;
@@ -44,6 +45,7 @@ typedef struct {
 	GtkWidget  *n_volume_checkbutton;
 	GtkWidget  *n_volume_spinbutton;
 	GtkWidget  *n_volume_box;
+	EggFileFormatChooser *format_chooser;
 } DlgNewData;
 
 
diff --git a/src/egg-macros.h b/src/egg-macros.h
new file mode 100644
index 0000000..a1dd396
--- /dev/null
+++ b/src/egg-macros.h
@@ -0,0 +1,154 @@
+/**
+ * Useful macros.
+ *
+ * Author:
+ *   Darin Adler <darin bentspoon com>
+ *
+ * Copyright 2001 Ben Tea Spoons, Inc.
+ */
+#ifndef _EGG_MACROS_H_
+#define _EGG_MACROS_H_
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* Macros for defining classes.  Ideas taken from Nautilus and GOB. */
+
+/* Define the boilerplate type stuff to reduce typos and code size.  Defines
+ * the get_type method and the parent_class static variable. */
+
+#define EGG_BOILERPLATE(type, type_as_function, corba_type,		\
+			   parent_type, parent_type_macro,		\
+			   register_type_macro)				\
+static void type_as_function ## _class_init    (type ## Class *klass);	\
+static void type_as_function ## _instance_init (type          *object);	\
+static parent_type ## Class *parent_class = NULL;			\
+static void								\
+type_as_function ## _class_init_trampoline (gpointer klass,		\
+					    gpointer data)		\
+{									\
+	parent_class = (parent_type ## Class *)g_type_class_ref (	\
+		parent_type_macro);					\
+	type_as_function ## _class_init ((type ## Class *)klass);	\
+}									\
+GType									\
+type_as_function ## _get_type (void)					\
+{									\
+	static GType object_type = 0;					\
+	if (object_type == 0) {						\
+		static const GTypeInfo object_info = {			\
+		    sizeof (type ## Class),				\
+		    NULL,		/* base_init */			\
+		    NULL,		/* base_finalize */		\
+		    type_as_function ## _class_init_trampoline,		\
+		    NULL,		/* class_finalize */		\
+		    NULL,               /* class_data */		\
+		    sizeof (type),					\
+		    0,                  /* n_preallocs */		\
+		    (GInstanceInitFunc) type_as_function ## _instance_init \
+		};							\
+		object_type = register_type_macro			\
+			(type, type_as_function, corba_type,		\
+			 parent_type, parent_type_macro);		\
+	}								\
+	return object_type;						\
+}
+
+/* Just call the parent handler.  This assumes that there is a variable
+ * named parent_class that points to the (duh!) parent class.  Note that
+ * this macro is not to be used with things that return something, use
+ * the _WITH_DEFAULT version for that */
+#define EGG_CALL_PARENT(parent_class_cast, name, args)		\
+	((parent_class_cast(parent_class)->name != NULL) ?		\
+	 parent_class_cast(parent_class)->name args : (void)0)
+
+/* Same as above, but in case there is no implementation, it evaluates
+ * to def_return */
+#define EGG_CALL_PARENT_WITH_DEFAULT(parent_class_cast,		\
+					name, args, def_return)		\
+	((parent_class_cast(parent_class)->name != NULL) ?		\
+	 parent_class_cast(parent_class)->name args : def_return)
+
+/* Call a virtual method */
+#define EGG_CALL_VIRTUAL(object, get_class_cast, method, args) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0)
+
+/* Call a virtual method with default */
+#define EGG_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default)
+
+#define EGG_CLASS_BOILERPLATE(type, type_as_function,		\
+				 parent_type, parent_type_macro)	\
+	EGG_BOILERPLATE(type, type_as_function, type,		\
+			   parent_type, parent_type_macro,		\
+			   EGG_REGISTER_TYPE)
+
+#define EGG_REGISTER_TYPE(type, type_as_function, corba_type,		\
+			    parent_type, parent_type_macro)		\
+	g_type_register_static (parent_type_macro, #type, &object_info, 0)
+
+
+#define EGG_DEFINE_BOXED_TYPE(TN, t_n) \
+EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, {});
+
+#define EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, _C_) \
+\
+static gpointer t_n##_copy (gpointer boxed); \
+static void t_n##_free (gpointer boxed); \
+\
+EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, t_n##_copy, t_n##_free, _C_);
+
+#define EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, b_c, b_f, _C_) \
+\
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TN, t_n, b_c, b_f) {_C_;} \
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_END()
+
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TypeName, type_name, boxed_copy, boxed_free) \
+\
+GType \
+type_name##_get_type (void) \
+{ \
+  static volatile gsize g_define_type_id__volatile = 0; \
+  if (g_once_init_enter (&g_define_type_id__volatile)) \
+    { \
+      GType g_define_type_id = \
+        g_boxed_type_register_static (g_intern_static_string (#TypeName), \
+                                      boxed_copy, boxed_free); \
+      { /* custom code follows */
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_END() \
+        /* following custom code */ \
+      }	\
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
+    } \
+  return g_define_type_id__volatile; \
+} /* closes type_name##_get_type() */
+
+#define EGG_DEFINE_QUARK(QN, q_n) \
+\
+GQuark \
+q_n##_quark (void) \
+{ \
+  static volatile gsize g_define_quark__volatile = 0; \
+  if (g_once_init_enter (&g_define_quark__volatile)) \
+    { \
+      GQuark g_define_quark = g_quark_from_string (#QN); \
+      g_once_init_leave (&g_define_quark__volatile, g_define_quark); \
+    } \
+  return g_define_quark__volatile; \
+}
+
+#define EGG_IS_POSITIVE_RESPONSE(response_id) \
+  ((response_id) == GTK_RESPONSE_ACCEPT || \
+   (response_id) == GTK_RESPONSE_OK     || \
+   (response_id) == GTK_RESPONSE_YES    || \
+   (response_id) == GTK_RESPONSE_APPLY)
+
+#define EGG_IS_NEGATIVE_RESPONSE(response_id) \
+  ((response_id) == GTK_RESPONSE_REJECT || \
+   (response_id) == GTK_RESPONSE_CANCEL || \
+   (response_id) == GTK_RESPONSE_NO)
+
+G_END_DECLS
+
+#endif /* _EGG_MACROS_H_ */
diff --git a/src/eggfileformatchooser.c b/src/eggfileformatchooser.c
new file mode 100644
index 0000000..200d5ef
--- /dev/null
+++ b/src/eggfileformatchooser.c
@@ -0,0 +1,1219 @@
+/* EggFileFormatChooser
+ * Copyright (C) 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "eggfileformatchooser.h"
+#include "egg-macros.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct _EggFileFormatFilterInfo EggFileFormatFilterInfo;
+typedef struct _EggFileFormatSearch EggFileFormatSearch;
+
+enum 
+{
+  MODEL_COLUMN_ID,
+  MODEL_COLUMN_NAME,
+  MODEL_COLUMN_ICON,
+  MODEL_COLUMN_EXTENSIONS,
+  MODEL_COLUMN_FILTER,
+  MODEL_COLUMN_DATA,
+  MODEL_COLUMN_DESTROY
+};
+
+enum 
+{
+  SIGNAL_SELECTION_CHANGED,
+  SIGNAL_LAST
+};
+
+struct _EggFileFormatChooserPrivate
+{
+  GtkTreeStore *model;
+  GtkTreeSelection *selection;
+  guint idle_hack;
+  guint last_id;
+
+  GtkFileChooser *chooser;
+  GtkFileFilter *all_files;
+  GtkFileFilter *supported_files;
+};
+
+struct _EggFileFormatFilterInfo
+{
+  GHashTable *extension_set;
+  GSList *extension_list;
+  gboolean show_extensions;
+  gchar *name;
+};
+
+struct _EggFileFormatSearch
+{
+  gboolean success;
+  GtkTreeIter iter;
+
+  guint format;
+  const gchar *extension;
+};
+
+static guint signals[SIGNAL_LAST];
+
+G_DEFINE_TYPE (EggFileFormatChooser, 
+	       egg_file_format_chooser,
+               GTK_TYPE_EXPANDER);
+static EGG_DEFINE_QUARK (EggFileFormatFilterInfo,
+                         egg_file_format_filter_info);
+
+static EggFileFormatFilterInfo*
+egg_file_format_filter_info_new (const gchar *name,
+                                 gboolean     show_extensions)
+{
+  EggFileFormatFilterInfo *self;
+
+  self = g_new0 (EggFileFormatFilterInfo, 1);
+  self->extension_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  self->show_extensions = show_extensions;
+  self->name = g_strdup (name);
+
+  return self;
+}
+
+static void
+egg_file_format_filter_info_free (gpointer boxed)
+{
+  EggFileFormatFilterInfo *self;
+
+  if (boxed)
+    {
+      self = boxed;
+
+      g_hash_table_unref (self->extension_set);
+      g_slist_foreach (self->extension_list, (GFunc) g_free, NULL);
+      g_slist_free (self->extension_list);
+      g_free (self->name);
+      g_free (self);
+    }
+}
+
+static gboolean
+egg_file_format_filter_find (gpointer key,
+                             gpointer value G_GNUC_UNUSED,
+                             gpointer data)
+{
+  const GtkFileFilterInfo *info = data;
+  const gchar *pattern = key;
+
+  return g_str_has_suffix (info->filename, pattern + 1);
+}
+
+static gboolean
+egg_file_format_filter_filter (const GtkFileFilterInfo *info,
+                               gpointer                 data)
+{
+  EggFileFormatFilterInfo *self = data;
+
+  return NULL != g_hash_table_find (self->extension_set,
+                                    egg_file_format_filter_find,
+                                    (gpointer) info);
+}
+
+static GtkFileFilter*
+egg_file_format_filter_new (const gchar *name,
+                            gboolean     show_extensions)
+{
+  GtkFileFilter *filter;
+  EggFileFormatFilterInfo *info;
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, name);
+
+  info = egg_file_format_filter_info_new (name, show_extensions);
+
+  gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME,
+                              egg_file_format_filter_filter,
+                              info, NULL);
+  g_object_set_qdata_full (G_OBJECT (filter),
+                           egg_file_format_filter_info_quark (),
+                           info, egg_file_format_filter_info_free);
+
+  return filter;
+}
+
+static void
+egg_file_format_filter_add_extensions (GtkFileFilter *filter,
+                                       const gchar   *extensions)
+{
+  EggFileFormatFilterInfo *info;
+  GString *filter_name;
+  const gchar *extptr;
+  gchar *pattern;
+  gsize length;
+
+  g_assert (NULL != extensions);
+
+  info = g_object_get_qdata (G_OBJECT (filter),
+                             egg_file_format_filter_info_quark ());
+
+  info->extension_list = g_slist_prepend (info->extension_list,
+                                          g_strdup (extensions));
+
+  if (info->show_extensions)
+    {
+      filter_name = g_string_new (info->name);
+      g_string_append (filter_name, " (");
+    }
+  else
+    filter_name = NULL;
+
+  extptr = extensions;
+  while (*extptr)
+    {
+      length = strcspn (extptr, ",");
+      pattern = g_new (gchar, length + 3);
+
+      memcpy (pattern, "*.", 2);
+      memcpy (pattern + 2, extptr, length);
+      pattern[length + 2] = '\0';
+
+      if (filter_name)
+        {
+          if (extptr != extensions)
+            g_string_append (filter_name, ", ");
+
+          g_string_append (filter_name, pattern);
+        }
+
+      extptr += length;
+
+      if (*extptr)
+        extptr += 2;
+
+      g_hash_table_replace (info->extension_set, pattern, pattern);
+    }
+
+  if (filter_name)
+    {
+      g_string_append (filter_name, ")");
+      gtk_file_filter_set_name (filter, filter_name->str);
+      g_string_free (filter_name, TRUE);
+    }
+}
+
+static void
+selection_changed_cb (GtkTreeSelection     *selection,
+                      EggFileFormatChooser *self)
+{
+  gchar *label;
+  gchar *name;
+
+  GtkFileFilter *filter;
+  GtkTreeModel *model;
+  GtkTreeIter parent;
+  GtkTreeIter iter;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter)) 
+    {
+      gtk_tree_model_get (model, &iter, MODEL_COLUMN_NAME, &name, -1);
+
+      label = g_strdup_printf (_("File _Format: %s"), name);
+      gtk_expander_set_use_underline (GTK_EXPANDER (self), TRUE);
+      gtk_expander_set_label (GTK_EXPANDER (self), label);
+
+      g_free (name);
+      g_free (label);
+
+      if (self->priv->chooser)
+        {
+          while (gtk_tree_model_iter_parent (model, &parent, &iter))
+            iter = parent;
+
+          gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+          gtk_file_chooser_set_filter (self->priv->chooser, filter);
+          g_object_unref (filter);
+        }
+
+      g_signal_emit (self, signals[SIGNAL_SELECTION_CHANGED], 0);
+    }
+}
+
+/* XXX This hack is needed, as gtk_expander_set_label seems 
+ * not to work from egg_file_format_chooser_init */
+static gboolean
+select_default_file_format (gpointer data)
+{
+  EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (data);
+  egg_file_format_chooser_set_format (self, 0);
+  self->priv->idle_hack = 0;
+  return FALSE;
+}
+
+static gboolean
+find_by_format (GtkTreeModel *model,
+                GtkTreePath  *path G_GNUC_UNUSED,
+                GtkTreeIter  *iter,
+                gpointer      data)
+{
+  EggFileFormatSearch *search = data;
+  guint id;
+
+  gtk_tree_model_get (model, iter, MODEL_COLUMN_ID, &id, -1);
+
+  if (id == search->format)
+    {
+      search->success = TRUE;
+      search->iter = *iter;
+    }
+
+  return search->success;
+}
+
+static gboolean
+find_in_list (gchar       *list,
+              const gchar *needle)
+{
+  gchar *saveptr;
+  gchar *token;
+
+  for (token = strtok_r (list, ",", &saveptr); NULL != token;
+       token = strtok_r (NULL, ",", &saveptr))
+    {
+      token = g_strstrip (token);
+
+      if (strcasecmp (needle, token) == 0)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+accept_filename (gchar       *extensions,
+                 const gchar *filename)
+{
+  const gchar *extptr;
+  gchar *saveptr;
+  gchar *token;
+  gsize length;
+
+  length = strlen (filename);
+
+  for (token = strtok_r (extensions, ",", &saveptr); NULL != token;
+       token = strtok_r (NULL, ",", &saveptr))
+    {
+      token = g_strstrip (token);
+      extptr = filename + length - strlen (token) - 1;
+
+      if (extptr > filename && '.' == *extptr &&
+          !strcmp (extptr + 1, token))
+          return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+find_by_extension (GtkTreeModel *model,
+                   GtkTreePath  *path G_GNUC_UNUSED,
+                   GtkTreeIter  *iter,
+                   gpointer      data)
+{
+  EggFileFormatSearch *search = data;
+
+  gchar *extensions = NULL;
+  guint format = 0;
+
+  gtk_tree_model_get (model, iter,
+                      MODEL_COLUMN_EXTENSIONS, &extensions,
+                      MODEL_COLUMN_ID, &format,
+                      -1);
+
+  if (extensions && find_in_list (extensions, search->extension))
+    {
+      search->format = format;
+      search->success = TRUE;
+      search->iter = *iter;
+    }
+
+  g_free (extensions);
+  return search->success;
+}
+
+static void
+expander_unmap_cb (GtkWidget *widget,
+		   gpointer   user_data)
+{
+  egg_file_format_chooser_emit_size_changed ((EggFileFormatChooser *)user_data);
+}
+
+static void
+egg_file_format_chooser_init (EggFileFormatChooser *self)
+{
+  GtkWidget *scroller;
+  GtkWidget *view;
+
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell;
+  GtkTreeIter iter;
+
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EGG_TYPE_FILE_FORMAT_CHOOSER, 
+                                            EggFileFormatChooserPrivate);
+
+/* file filters */
+
+  self->priv->all_files = g_object_ref_sink (gtk_file_filter_new ());
+  gtk_file_filter_set_name (self->priv->all_files, _("All Files"));
+  self->priv->supported_files = egg_file_format_filter_new (_("All Supported Files"), FALSE);
+
+/* tree model */
+
+  self->priv->model = gtk_tree_store_new (7, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+                                             GTK_TYPE_FILE_FILTER, G_TYPE_POINTER, G_TYPE_POINTER);
+
+  gtk_tree_store_append (self->priv->model, &iter, NULL);
+  gtk_tree_store_set (self->priv->model, &iter,
+                      MODEL_COLUMN_NAME, _("By Extension"),
+                      MODEL_COLUMN_FILTER, self->priv->supported_files,
+                      MODEL_COLUMN_ID, 0,
+                      -1);
+
+/* tree view */
+
+  view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->priv->model));
+  self->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
+
+/* file format column */
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_expand (column, TRUE);
+  gtk_tree_view_column_set_title (column, _("File Format"));
+  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+  cell = gtk_cell_renderer_pixbuf_new ();
+  gtk_tree_view_column_pack_start (column, cell, FALSE);
+  gtk_tree_view_column_set_attributes (column, cell,
+                                       "icon-name", MODEL_COLUMN_ICON,
+                                       NULL);
+
+  cell = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, cell, TRUE);
+  gtk_tree_view_column_set_attributes (column, cell,
+                                       "text", MODEL_COLUMN_NAME,
+                                       NULL);
+
+/* extensions column */
+
+  column = gtk_tree_view_column_new_with_attributes (
+    _("Extension(s)"), gtk_cell_renderer_text_new (),
+    "text", MODEL_COLUMN_EXTENSIONS, NULL);
+  gtk_tree_view_column_set_expand (column, FALSE);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+/* selection */
+
+  gtk_tree_selection_set_mode (self->priv->selection, GTK_SELECTION_BROWSE);
+  g_signal_connect (self->priv->selection, "changed",
+                    G_CALLBACK (selection_changed_cb), self);
+  self->priv->idle_hack = g_idle_add (select_default_file_format, self);
+
+/* scroller */
+
+  scroller = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
+                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
+                                       GTK_SHADOW_IN);
+  gtk_widget_set_size_request (scroller, -1, 150);
+  gtk_container_add (GTK_CONTAINER (scroller), view);
+  gtk_widget_show_all (scroller);
+
+  gtk_container_add (GTK_CONTAINER (self), scroller);
+
+  g_signal_connect_after (scroller, "unmap", G_CALLBACK (expander_unmap_cb), self);
+}
+
+static void
+reset_model (EggFileFormatChooser *self)
+{
+  GtkTreeModel *model = GTK_TREE_MODEL (self->priv->model);
+  GtkTreeIter iter;
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        {
+          GDestroyNotify destroy = NULL;
+          gpointer data = NULL;
+
+          gtk_tree_model_get (model, &iter,
+                              MODEL_COLUMN_DESTROY, &destroy,
+                              MODEL_COLUMN_DATA, &data,
+                              -1);
+
+          if (destroy)
+            destroy (data);
+        }
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+
+  gtk_tree_store_clear (self->priv->model);
+}
+
+static void
+egg_file_format_chooser_dispose (GObject *obj)
+{
+  EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj);
+
+  if (NULL != self)
+    {
+      if (self->priv->idle_hack)
+        {
+          g_source_remove (self->priv->idle_hack);
+          self->priv->idle_hack = 0;
+        }
+    }
+
+  G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->dispose (obj);
+}
+
+static void
+egg_file_format_chooser_finalize (GObject *obj)
+{
+  EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj);
+
+  if (NULL != self)
+    {
+      if (self->priv->model)
+        {
+          reset_model (self);
+
+          g_object_unref (self->priv->model);
+          self->priv->model = NULL;
+
+          g_object_unref (self->priv->all_files);
+          self->priv->all_files = NULL;
+        }
+    }
+
+  G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->finalize (obj);
+}
+
+static void
+filter_changed_cb (GObject    *object,
+                   GParamSpec *spec,
+                   gpointer    data)
+{
+  EggFileFormatChooser *self;
+
+  GtkFileFilter *current_filter;
+  GtkFileFilter *format_filter;
+
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  GtkTreeIter parent;
+
+  self = EGG_FILE_FORMAT_CHOOSER (data);
+
+  format_filter = NULL;
+  current_filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (object));
+  model = GTK_TREE_MODEL (self->priv->model);
+
+  if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter)) 
+    {
+      while (gtk_tree_model_iter_parent (model, &parent, &iter))
+        iter = parent;
+
+      gtk_tree_model_get (model, &iter,
+                          MODEL_COLUMN_FILTER,
+                          &format_filter, -1);
+      g_object_unref (format_filter);
+    }
+
+  if (current_filter && current_filter != format_filter &&
+      gtk_tree_model_get_iter_first (model, &iter))
+    {
+      if (current_filter == self->priv->all_files)
+        format_filter = current_filter;
+      else
+        {
+          format_filter = NULL;
+
+          do
+            {
+              gtk_tree_model_get (model, &iter,
+                                  MODEL_COLUMN_FILTER,
+                                  &format_filter, -1);
+              g_object_unref (format_filter);
+
+              if (format_filter == current_filter)
+                break;
+            }
+          while (gtk_tree_model_iter_next (model, &iter));
+        }
+
+      if (format_filter)
+        gtk_tree_selection_select_iter (self->priv->selection, &iter);
+    }
+}
+
+/* Shows an error dialog set as transient for the specified window */
+static void
+error_message_with_parent (GtkWindow  *parent,
+			   const char *msg,
+			   const char *detail)
+{
+  gboolean first_call = TRUE;
+  GtkWidget *dialog;
+
+  if (first_call)
+    {
+      g_warning ("%s: Merge with the code in Gtk{File,Recent}ChooserDefault.", G_STRLOC);
+      first_call = FALSE;
+    }
+
+  dialog = gtk_message_dialog_new (parent,
+				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+				   GTK_MESSAGE_ERROR,
+				   GTK_BUTTONS_OK,
+				   "%s",
+				   msg);
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+					    "%s", detail);
+
+  if (parent->group)
+    gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+}
+
+/* Returns a toplevel GtkWindow, or NULL if none */
+static GtkWindow *
+get_toplevel (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (!GTK_WIDGET_TOPLEVEL (toplevel))
+    return NULL;
+  else
+    return GTK_WINDOW (toplevel);
+}
+
+/* Shows an error dialog for the file chooser */
+static void
+error_message (EggFileFormatChooser *impl,
+	       const char           *msg,
+	       const char           *detail)
+{
+  error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
+}
+
+static void
+chooser_response_cb (GtkDialog *dialog,
+                     gint       response_id,
+                     gpointer   data)
+{
+  EggFileFormatChooser *self;
+  gchar *filename, *basename;
+  gchar *message;
+  guint format;
+
+  self = EGG_FILE_FORMAT_CHOOSER (data);
+
+  if (EGG_IS_POSITIVE_RESPONSE (response_id))
+    {
+      filename = gtk_file_chooser_get_filename (self->priv->chooser);
+      basename = g_filename_display_basename (filename);
+      g_free (filename);
+
+      format = egg_file_format_chooser_get_format (self, basename);
+      g_print ("%s: %s - %d\n", G_STRFUNC, basename, format);
+
+      if (0 == format)
+        {
+
+          message = g_strdup_printf (
+            _("The program was not able to find out the file format "
+              "you want to use for `%s'. Please make sure to use a "
+              "known extension for that file or manually choose a "
+              "file format from the list below."),
+              basename);
+
+          error_message (self,
+		         _("File format not recognized"),
+                        message);
+
+          g_free (message);
+
+          g_signal_stop_emission_by_name (dialog, "response");
+        }
+      else
+        {
+          filename = egg_file_format_chooser_append_extension (self, basename, format);
+
+          if (strcmp (filename, basename))
+            {
+              gtk_file_chooser_set_current_name (self->priv->chooser, filename);
+              g_signal_stop_emission_by_name (dialog, "response");
+            }
+
+          g_free (filename);
+        }
+
+      g_free (basename); 
+    }
+
+}
+
+static void
+egg_file_format_chooser_realize (GtkWidget *widget)
+{
+  EggFileFormatChooser *self;
+  GtkWidget *parent;
+
+  GtkFileFilter *filter;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->realize (widget);
+
+  self = EGG_FILE_FORMAT_CHOOSER (widget);
+
+  g_return_if_fail (NULL == self->priv->chooser);
+
+  parent = gtk_widget_get_toplevel (widget);
+
+  if (!GTK_IS_FILE_CHOOSER (parent))
+    parent = gtk_widget_get_parent (widget);
+
+  while (parent && !GTK_IS_FILE_CHOOSER (parent))
+    parent = gtk_widget_get_parent (parent);
+
+  self->priv->chooser = GTK_FILE_CHOOSER (parent);
+
+  g_return_if_fail (GTK_IS_FILE_CHOOSER (self->priv->chooser));
+  g_return_if_fail (gtk_file_chooser_get_action (self->priv->chooser) ==
+                    GTK_FILE_CHOOSER_ACTION_SAVE);
+
+  g_object_ref (self->priv->chooser);
+
+  g_signal_connect (self->priv->chooser, "notify::filter", 
+                    G_CALLBACK (filter_changed_cb), self);
+  gtk_file_chooser_add_filter (self->priv->chooser, self->priv->all_files);
+
+  model = GTK_TREE_MODEL (self->priv->model);
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        {
+          gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+          gtk_file_chooser_add_filter (self->priv->chooser, filter);
+          g_object_unref (filter);
+        }
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+
+  gtk_file_chooser_set_filter (self->priv->chooser,
+                               self->priv->supported_files);
+
+  if (GTK_IS_DIALOG (self->priv->chooser))
+    g_signal_connect (self->priv->chooser, "response",
+                      G_CALLBACK (chooser_response_cb), self);
+}
+
+static void
+egg_file_format_chooser_unrealize (GtkWidget *widget)
+{
+  EggFileFormatChooser *self;
+
+  GtkFileFilter *filter;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->unrealize (widget);
+
+  self = EGG_FILE_FORMAT_CHOOSER (widget);
+  model = GTK_TREE_MODEL (self->priv->model);
+
+  g_signal_handlers_disconnect_by_func (self->priv->chooser,
+                                        filter_changed_cb, self);
+  g_signal_handlers_disconnect_by_func (self->priv->chooser,
+                                        chooser_response_cb, self);
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        {
+          gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+          gtk_file_chooser_remove_filter (self->priv->chooser, filter);
+          g_object_unref (filter);
+        }
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+
+  gtk_file_chooser_remove_filter (self->priv->chooser, self->priv->all_files);
+  g_object_unref (self->priv->chooser);
+}
+
+static void
+egg_file_format_chooser_class_init (EggFileFormatChooserClass *cls)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (cls);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (cls);
+
+  g_type_class_add_private (cls, sizeof (EggFileFormatChooserPrivate));
+
+  object_class->dispose = egg_file_format_chooser_dispose;
+  object_class->finalize = egg_file_format_chooser_finalize;
+
+  widget_class->realize = egg_file_format_chooser_realize;
+  widget_class->unrealize = egg_file_format_chooser_unrealize;
+
+  signals[SIGNAL_SELECTION_CHANGED] = g_signal_new (
+    "selection-changed", EGG_TYPE_FILE_FORMAT_CHOOSER, G_SIGNAL_RUN_FIRST,
+    G_STRUCT_OFFSET (EggFileFormatChooserClass, selection_changed),
+    NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+GtkWidget*
+egg_file_format_chooser_new (void)
+{
+  return g_object_new (EGG_TYPE_FILE_FORMAT_CHOOSER, NULL);
+}
+
+static guint
+egg_file_format_chooser_add_format_impl (EggFileFormatChooser *self,
+                                         guint                 parent,
+                                         const gchar          *name,
+                                         const gchar          *icon,
+                                         const gchar          *extensions)
+{
+  EggFileFormatSearch search;
+  GtkFileFilter *filter;
+  GtkTreeIter iter;
+
+  search.success = FALSE;
+  search.format = parent;
+  filter = NULL;
+
+  if (parent > 0)
+    {
+      gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model),
+                              find_by_format, &search);
+      g_return_val_if_fail (search.success, -1);
+    }
+  else
+    filter = egg_file_format_filter_new (name, TRUE);
+
+  gtk_tree_store_append (self->priv->model, &iter, 
+                         parent > 0 ? &search.iter : NULL);
+
+  gtk_tree_store_set (self->priv->model, &iter,
+                      MODEL_COLUMN_ID, ++self->priv->last_id,
+                      MODEL_COLUMN_EXTENSIONS, extensions,
+                      MODEL_COLUMN_FILTER, filter,
+                      MODEL_COLUMN_NAME, name,
+                      MODEL_COLUMN_ICON, icon,
+                      -1);
+
+  if (extensions)
+    {
+      if (parent > 0)
+        gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model), &search.iter,
+                            MODEL_COLUMN_FILTER, &filter, -1);
+
+      egg_file_format_filter_add_extensions (self->priv->supported_files, extensions);
+      egg_file_format_filter_add_extensions (filter, extensions);
+
+      if (parent > 0)
+        g_object_unref (filter);
+    }
+
+  return self->priv->last_id;
+}
+
+guint
+egg_file_format_chooser_add_format (EggFileFormatChooser *self,
+                                    guint                 parent,
+                                    const gchar          *name,
+                                    const gchar          *icon,
+                                    ...)
+{
+  GString *buffer = NULL;
+  const gchar* extptr;
+  va_list extensions;
+  guint id;
+
+  g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), 0);
+  g_return_val_if_fail (NULL != name, 0);
+
+  va_start (extensions, icon);
+
+  while (NULL != (extptr = va_arg (extensions, const gchar*)))
+    {
+      if (NULL == buffer)
+        buffer = g_string_new (NULL);
+      else
+        g_string_append (buffer, ", ");
+
+      g_string_append (buffer, extptr);
+    }
+
+  va_end (extensions);
+
+  id = egg_file_format_chooser_add_format_impl (self, parent, name, icon,
+                                                buffer ? buffer->str : NULL);
+
+  if (buffer)
+    g_string_free (buffer, TRUE);
+
+  return id;
+}
+
+static gchar*
+get_icon_name (const gchar *mime_type)
+{
+  static gboolean first_call = TRUE;
+  gchar *name = NULL;
+  gchar *s;
+
+  if (first_call)
+    {
+      g_warning ("%s: Replace by g_content_type_get_icon "
+                 "when GVFS is merged into GLib.", G_STRLOC);
+      first_call = FALSE;
+    }
+
+  if (mime_type)
+    {
+      name = g_strconcat ("gnome-mime-", mime_type, NULL);
+
+      for(s = name; *s; ++s)
+        {
+          if (!isalpha (*s) || !isascii (*s))
+            *s = '-';
+        }
+    }
+
+  if (!name ||
+      !gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name))
+    {
+      g_free (name);
+      name = g_strdup ("gnome-mime-image");
+    }
+
+  return name;
+}
+
+void           
+egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self,
+                                            guint                 parent G_GNUC_UNUSED,
+                                            guint               **formats)
+{
+  GSList *pixbuf_formats = NULL;
+  GSList *iter;
+  gint i;
+
+  g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+  pixbuf_formats = gdk_pixbuf_get_formats ();
+
+  if (formats)
+    *formats = g_new0 (guint, g_slist_length (pixbuf_formats) + 1);
+
+  for(iter = pixbuf_formats, i = 0; iter; iter = iter->next, ++i)
+    {
+      GdkPixbufFormat *format = iter->data;
+
+      gchar *description, *name, *extensions, *icon;
+      gchar **mime_types, **extension_list;
+      guint id;
+
+      if (gdk_pixbuf_format_is_disabled (format) ||
+         !gdk_pixbuf_format_is_writable (format))
+        continue;
+
+      mime_types = gdk_pixbuf_format_get_mime_types (format);
+      icon = get_icon_name (mime_types[0]);
+      g_strfreev (mime_types);
+
+      extension_list = gdk_pixbuf_format_get_extensions (format);
+      extensions = g_strjoinv (", ", extension_list);
+      g_strfreev (extension_list);
+
+      description = gdk_pixbuf_format_get_description (format);
+      name = gdk_pixbuf_format_get_name (format);
+
+      id = egg_file_format_chooser_add_format_impl (self, parent, description, 
+                                                    icon, extensions);
+
+      g_free (description);
+      g_free (extensions);
+      g_free (icon);
+
+      egg_file_format_chooser_set_format_data (self, id, name, g_free);
+
+      if (formats)
+        *formats[i] = id;
+    }
+
+  g_slist_free (pixbuf_formats);
+}
+
+void
+egg_file_format_chooser_remove_format (EggFileFormatChooser *self,
+                                       guint                 format)
+{
+  GDestroyNotify destroy = NULL;
+  gpointer data = NULL;
+
+  EggFileFormatSearch search;
+  GtkFileFilter *filter;
+  GtkTreeModel *model;
+
+  g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+  search.success = FALSE;
+  search.format = format;
+
+  model = GTK_TREE_MODEL (self->priv->model);
+  gtk_tree_model_foreach (model, find_by_format, &search);
+
+  g_return_if_fail (search.success);
+
+  gtk_tree_model_get (model, &search.iter,
+                      MODEL_COLUMN_FILTER, &filter,
+                      MODEL_COLUMN_DESTROY, &destroy,
+                      MODEL_COLUMN_DATA, &data,
+                      -1);
+
+  if (destroy)
+    destroy (data);
+
+  if (filter)
+    {
+      if (self->priv->chooser)
+        gtk_file_chooser_remove_filter (self->priv->chooser, filter);
+
+      g_object_unref (filter);
+    }
+  else
+    g_warning ("TODO: Remove extensions from parent filter");
+
+  gtk_tree_store_remove (self->priv->model, &search.iter);
+}
+
+void            
+egg_file_format_chooser_set_format (EggFileFormatChooser *self,
+                                    guint                 format)
+{
+  EggFileFormatSearch search;
+
+  GtkTreeModel *model;
+  GtkTreePath *path;
+  GtkTreeView *view;
+
+  g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+  search.success = FALSE;
+  search.format = format;
+
+  model = GTK_TREE_MODEL (self->priv->model);
+  gtk_tree_model_foreach (model, find_by_format, &search);
+
+  g_return_if_fail (search.success);
+
+  path = gtk_tree_model_get_path (model, &search.iter);
+  view = gtk_tree_selection_get_tree_view (self->priv->selection);
+
+  gtk_tree_view_expand_to_path (view, path);
+  gtk_tree_selection_unselect_all (self->priv->selection);
+  gtk_tree_selection_select_path (self->priv->selection, path);
+
+  gtk_tree_path_free (path);
+
+  if (self->priv->idle_hack > 0)
+    {
+      g_source_remove (self->priv->idle_hack);
+      self->priv->idle_hack = 0;
+    }
+}
+
+guint
+egg_file_format_chooser_get_format (EggFileFormatChooser *self,
+                                    const gchar          *filename)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  guint format = 0;
+
+  g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), -1);
+
+  if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter))
+    gtk_tree_model_get (model, &iter, MODEL_COLUMN_ID, &format, -1);
+
+  if (0 == format && NULL != filename)
+    {
+      EggFileFormatSearch search;
+
+      search.extension = strrchr(filename, '.');
+      search.success = FALSE;
+
+      if (search.extension++)
+        gtk_tree_model_foreach (model, find_by_extension, &search);
+      if (search.success)
+        format = search.format;
+    }
+
+  return format;
+}
+
+void            
+egg_file_format_chooser_set_format_data (EggFileFormatChooser *self,
+                                         guint                 format,
+                                         gpointer              data,
+                                         GDestroyNotify        destroy)
+{
+  EggFileFormatSearch search;
+
+  g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+  search.success = FALSE;
+  search.format = format;
+
+  gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model),
+                          find_by_format, &search);
+
+  g_return_if_fail (search.success);
+
+  gtk_tree_store_set (self->priv->model, &search.iter,
+                      MODEL_COLUMN_DESTROY, destroy,
+                      MODEL_COLUMN_DATA, data,
+                      -1);
+}
+
+gpointer
+egg_file_format_chooser_get_format_data (EggFileFormatChooser *self,
+                                         guint                 format)
+{
+  EggFileFormatSearch search;
+  gpointer data = NULL;
+  GtkTreeModel *model;
+
+  g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL);
+
+  search.success = FALSE;
+  search.format = format;
+
+  model = GTK_TREE_MODEL (self->priv->model);
+  gtk_tree_model_foreach (model, find_by_format, &search);
+
+  g_return_val_if_fail (search.success, NULL);
+
+  gtk_tree_model_get (model, &search.iter,
+                      MODEL_COLUMN_DATA, &data,
+                      -1);
+  return data;
+}
+
+gchar*
+egg_file_format_chooser_append_extension (EggFileFormatChooser *self,
+                                          const gchar          *filename,
+                                          guint                 format)
+{
+  EggFileFormatSearch search;
+  GtkTreeModel *model;
+  GtkTreeIter child;
+
+  gchar *extensions;
+  gchar *result;
+
+  g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL);
+  g_return_val_if_fail (NULL != filename, NULL);
+
+  if (0 == format)
+    format = egg_file_format_chooser_get_format (self, NULL);
+
+  if (0 == format)
+    {
+      g_warning ("%s: No file format selected. Cannot append extension.", G_STRFUNC);
+      return NULL;
+    }
+
+  search.success = FALSE;
+  search.format = format;
+
+  model = GTK_TREE_MODEL (self->priv->model);
+  gtk_tree_model_foreach (model, find_by_format, &search);
+
+  g_return_val_if_fail (search.success, NULL);
+
+  gtk_tree_model_get (model, &search.iter, 
+                      MODEL_COLUMN_EXTENSIONS, &extensions,
+                      -1);
+
+  if (NULL == extensions && 
+      gtk_tree_model_iter_nth_child (model, &child, &search.iter, 0))
+    {
+      gtk_tree_model_get (model, &child, 
+                          MODEL_COLUMN_EXTENSIONS, &extensions,
+                          -1);
+    }
+
+  if (NULL == extensions)
+    {
+      g_warning ("%s: File format %d doesn't provide file extensions. "
+                 "Cannot append extension.", G_STRFUNC, format);
+      return NULL;
+    }
+
+  if (accept_filename (extensions, filename))
+    result = g_strdup (filename);
+  else
+    result = g_strconcat (filename, ".", extensions, NULL);
+
+  g_assert (NULL == strchr(extensions, ','));
+  g_free (extensions);
+  return result;
+}
+
+static int
+emit_default_size_changed (gpointer data)
+{
+  if (GTK_IS_FILE_CHOOSER (data))
+    g_signal_emit_by_name (data, "default-size-changed");
+  return FALSE;
+}
+
+void
+egg_file_format_chooser_emit_size_changed (EggFileFormatChooser *self)
+{
+  GtkWidget *parent;
+
+  parent = gtk_widget_get_parent (GTK_WIDGET (self));
+  while ((parent != NULL) && !GTK_IS_FILE_CHOOSER (parent))
+    parent = gtk_widget_get_parent (parent);
+  if (parent != NULL)
+    gdk_threads_add_idle (emit_default_size_changed, parent);
+}
+
+/* vim: set sw=2 sta et: */
diff --git a/src/eggfileformatchooser.h b/src/eggfileformatchooser.h
new file mode 100644
index 0000000..6c78891
--- /dev/null
+++ b/src/eggfileformatchooser.h
@@ -0,0 +1,85 @@
+/* EggFileFormatChooser
+ * Copyright (C) 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __EGG_FILE_FORMAT_CHOOSER_H__
+#define __EGG_FILE_FORMAT_CHOOSER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_FILE_FORMAT_CHOOSER           (egg_file_format_chooser_get_type())
+#define EGG_FILE_FORMAT_CHOOSER(obj)           (G_TYPE_CHECK_INSTANCE_CAST(obj, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooser))
+#define EGG_FILE_FORMAT_CHOOSER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST(klass, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass))
+#define EGG_IS_FILE_FORMAT_CHOOSER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER))
+#define EGG_IS_FILE_FORMAT_CHOOSER_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER))
+#define EGG_FILE_FORMAT_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass))
+
+typedef struct _EggFileFormatChooser        EggFileFormatChooser;
+typedef struct _EggFileFormatChooserClass   EggFileFormatChooserClass;
+typedef struct _EggFileFormatChooserPrivate EggFileFormatChooserPrivate;
+
+struct _EggFileFormatChooser
+{
+  GtkExpander parent;
+  EggFileFormatChooserPrivate *priv;
+};
+
+struct _EggFileFormatChooserClass
+{
+  GtkExpanderClass parent;
+
+  void (*selection_changed)(EggFileFormatChooser *self);
+};
+
+GType           egg_file_format_chooser_get_type           (void) G_GNUC_CONST;
+GtkWidget*      egg_file_format_chooser_new                (void);
+
+guint           egg_file_format_chooser_add_format         (EggFileFormatChooser *self,
+                                                            guint                 parent,
+                                                            const gchar          *name,
+                                                            const gchar          *icon,
+                                                            ...) G_GNUC_NULL_TERMINATED;
+void            egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self,
+                                                            guint                 parent,
+                                                            guint               **formats);
+void            egg_file_format_chooser_remove_format      (EggFileFormatChooser *self,
+                                                            guint                 format);
+
+void            egg_file_format_chooser_set_format         (EggFileFormatChooser *self,
+                                                            guint                 format);
+guint           egg_file_format_chooser_get_format         (EggFileFormatChooser *self,
+                                                            const gchar          *filename);
+
+void            egg_file_format_chooser_set_format_data    (EggFileFormatChooser *self,
+                                                            guint                 format,
+                                                            gpointer              data,
+                                                            GDestroyNotify        destroy);
+gpointer        egg_file_format_chooser_get_format_data    (EggFileFormatChooser *self,
+                                                            guint                 format);
+
+gchar*          egg_file_format_chooser_append_extension   (EggFileFormatChooser *self,
+                                                            const gchar          *filename,
+                                                            guint                 format);
+void            egg_file_format_chooser_emit_size_changed  (EggFileFormatChooser *self);
+
+G_END_DECLS
+
+#endif /* __EGG_FILE_FORMAT_CHOOSER_H__ */
+
+/* vim: set sw=2 sta et: */
diff --git a/src/main.c b/src/main.c
index b013ed5..04b2951 100644
--- a/src/main.c
+++ b/src/main.c
@@ -172,7 +172,8 @@ FrExtensionType file_ext_type[] = {
 	{ ".z", "application/x-gzip" },
 	{ ".Z", "application/x-compress" },
 	{ ".zip", "application/zip" },
-	{ ".zoo", "application/x-zoo" }
+	{ ".zoo", "application/x-zoo" },
+	{ NULL, NULL }
 };
 
 int single_file_save_type[64];
@@ -695,9 +696,12 @@ get_mime_type_from_extension (const char *ext)
 	if (ext == NULL)
 		return NULL;
 
-	for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--)
+	for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--) {
+		if (file_ext_type[i].ext == NULL)
+			continue;
 		if (strcasecmp (ext, file_ext_type[i].ext) == 0)
 			return get_static_string (file_ext_type[i].mime_type);
+	}
 
 	return NULL;
 }
@@ -716,9 +720,13 @@ get_archive_filename_extension (const char *filename)
 	if (ext == NULL)
 		return NULL;
 
-	for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--)
+	for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--) {
+		if (file_ext_type[i].ext == NULL)
+			continue;
 		if (strcasecmp (ext, file_ext_type[i].ext) == 0)
 			return ext;
+	}
+
 	return NULL;
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]