changed ASSERT_MESSAGE and ERROR_MESSAGE macros to use proper
[xonotic/netradiant.git] / radiant / dialog.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 //
23 // Base dialog class, provides a way to run modal dialogs and
24 // set/get the widget values in member variables.
25 //
26 // Leonardo Zide (leo@lokigames.com)
27 //
28
29 #include "dialog.h"
30
31 #include "debugging/debugging.h"
32
33
34 #include "mainframe.h"
35
36 #include <stdlib.h>
37
38 #include <gtk/gtkmain.h>
39 #include <gtk/gtkvbox.h>
40 #include <gtk/gtkhbox.h>
41 #include <gtk/gtktogglebutton.h>
42 #include <gtk/gtkspinbutton.h>
43 #include <gtk/gtkradiobutton.h>
44 #include <gtk/gtkentry.h>
45 #include <gtk/gtkcombobox.h>
46 #include <gtk/gtklabel.h>
47 #include <gtk/gtktable.h>
48 #include <gtk/gtkhscale.h>
49 #include <gtk/gtkalignment.h>
50
51 #include "stream/stringstream.h"
52 #include "convert.h"
53 #include "gtkutil/dialog.h"
54 #include "gtkutil/button.h"
55 #include "gtkutil/entry.h"
56 #include "gtkutil/image.h"
57
58 #include "gtkmisc.h"
59
60
61 GtkEntry* DialogEntry_new()
62 {
63   GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
64   gtk_widget_show(GTK_WIDGET(entry));
65   gtk_widget_set_size_request(GTK_WIDGET(entry), 64, -1);
66   return entry;
67 }
68
69 class DialogEntryRow
70 {
71 public:
72   DialogEntryRow(GtkWidget* row, GtkEntry* entry) : m_row(row), m_entry(entry)
73   {
74   }
75   GtkWidget* m_row;
76   GtkEntry* m_entry;
77 };
78
79 DialogEntryRow DialogEntryRow_new(const char* name)
80 {
81   GtkWidget* alignment = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
82   gtk_widget_show(alignment);
83
84   GtkEntry* entry = DialogEntry_new();
85   gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(entry));
86
87   return DialogEntryRow(GTK_WIDGET(DialogRow_new(name, alignment)), entry);
88 }
89
90
91 GtkSpinButton* DialogSpinner_new(double value, double lower, double upper, int fraction)
92 {
93   double step = 1.0 / double(fraction);
94   unsigned int digits = 0;
95   for(;fraction > 1; fraction /= 10)
96   {
97     ++digits;
98   }
99   GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(value, lower, upper, step, 10, 10)), step, digits)); 
100   gtk_widget_show(GTK_WIDGET(spin));
101   gtk_widget_set_size_request(GTK_WIDGET(spin), 64, -1);
102   return spin;
103 }
104
105 class DialogSpinnerRow
106 {
107 public:
108   DialogSpinnerRow(GtkWidget* row, GtkSpinButton* spin) : m_row(row), m_spin(spin)
109   {
110   }
111   GtkWidget* m_row;
112   GtkSpinButton* m_spin;
113 };
114
115 DialogSpinnerRow DialogSpinnerRow_new(const char* name, double value, double lower, double upper, int fraction)
116 {
117   GtkWidget* alignment = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
118   gtk_widget_show(alignment);
119
120   GtkSpinButton* spin = DialogSpinner_new(value, lower, upper, fraction);
121   gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(spin));
122
123   return DialogSpinnerRow(GTK_WIDGET(DialogRow_new(name, alignment)), spin);
124 }
125
126
127
128 template<
129   typename Type_,
130   typename Other_,
131   void(*Import)(Type_&, Other_),
132   void(*Export)(Type_&, const Callback1<Other_>&)
133 >
134 class ImportExport
135 {
136 public:
137   typedef Type_ Type;
138   typedef Other_ Other;
139
140   typedef ReferenceCaller1<Type, Other, Import> ImportCaller;
141   typedef ReferenceCaller1<Type, const Callback1<Other>&, Export> ExportCaller;
142 };
143
144 typedef ImportExport<bool, bool, BoolImport, BoolExport> BoolImportExport;
145 typedef ImportExport<int, int, IntImport, IntExport> IntImportExport;
146 typedef ImportExport<std::size_t, std::size_t, SizeImport, SizeExport> SizeImportExport;
147 typedef ImportExport<float, float, FloatImport, FloatExport> FloatImportExport;
148 typedef ImportExport<CopiedString, const char*, StringImport, StringExport> StringImportExport;
149
150
151
152 void BoolToggleImport(GtkToggleButton& widget, bool value)
153 {
154   gtk_toggle_button_set_active(&widget, value);
155 }
156 void BoolToggleExport(GtkToggleButton& widget, const BoolImportCallback& importCallback)
157 {
158   importCallback(gtk_toggle_button_get_active(&widget) != FALSE);
159 }
160 typedef ImportExport<GtkToggleButton, bool, BoolToggleImport, BoolToggleExport> BoolToggleImportExport;
161
162
163 void IntRadioImport(GtkRadioButton& widget, int index)
164 {
165   radio_button_set_active(&widget, index);
166 }
167 void IntRadioExport(GtkRadioButton& widget, const IntImportCallback& importCallback)
168 {
169   importCallback(radio_button_get_active(&widget));
170 }
171 typedef ImportExport<GtkRadioButton, int, IntRadioImport, IntRadioExport> IntRadioImportExport;
172
173 template<typename Type, typename Formatter>
174 class StringFromType
175 {
176   StringOutputStream value;
177 public:
178   StringFromType(const Type& type)
179   {
180     value << Formatter(type);
181   }
182   operator const char*() const
183   {
184     return value.c_str();
185   }
186 };
187
188 typedef StringFromType<const char*, ConvertLocaleToUTF8> LocaleToUTF8;
189 typedef StringFromType<const char*, ConvertUTF8ToLocale> UTF8ToLocale;
190
191 void TextEntryImport(GtkEntry& widget, const char* text)
192 {
193   gtk_entry_set_text(&widget, LocaleToUTF8(text));
194 }
195 void TextEntryExport(GtkEntry& widget, const StringImportCallback& importCallback)
196 {
197   importCallback(UTF8ToLocale(gtk_entry_get_text(&widget)));
198 }
199 typedef ImportExport<GtkEntry, const char*, TextEntryImport, TextEntryExport> TextEntryImportExport;
200
201
202 void IntEntryImport(GtkEntry& widget, int value)
203 {
204   entry_set_int(&widget, value);
205 }
206 void IntEntryExport(GtkEntry& widget, const IntImportCallback& importCallback)
207 {
208   importCallback(atoi(gtk_entry_get_text (&widget)));
209 }
210 typedef ImportExport<GtkEntry, int, IntEntryImport, IntEntryExport> IntEntryImportExport;
211
212
213 void SizeEntryImport(GtkEntry& widget, std::size_t value)
214 {
215   entry_set_int(&widget, int(value));
216 }
217 void SizeEntryExport(GtkEntry& widget, const SizeImportCallback& importCallback)
218 {
219   int value = atoi(gtk_entry_get_text(&widget));
220   if(value < 0)
221   {
222     value = 0;
223   }
224   importCallback(value);
225 }
226 typedef ImportExport<GtkEntry, std::size_t, SizeEntryImport, SizeEntryExport> SizeEntryImportExport;
227
228
229 void FloatEntryImport(GtkEntry& widget, float value)
230 {
231   entry_set_float(&widget, value);
232 }
233 void FloatEntryExport(GtkEntry& widget, const FloatImportCallback& importCallback)
234 {
235   importCallback((float)atof(gtk_entry_get_text(&widget)));
236 }
237 typedef ImportExport<GtkEntry, float, FloatEntryImport, FloatEntryExport> FloatEntryImportExport;
238
239
240 void FloatSpinnerImport(GtkSpinButton& widget, float value)
241 {
242   gtk_spin_button_set_value(&widget, value);
243 }
244 void FloatSpinnerExport(GtkSpinButton& widget, const FloatImportCallback& importCallback)
245 {
246   importCallback(float(gtk_spin_button_get_value_as_float(&widget)));
247 }
248 typedef ImportExport<GtkSpinButton, float, FloatSpinnerImport, FloatSpinnerExport> FloatSpinnerImportExport;
249
250
251 void IntSpinnerImport(GtkSpinButton& widget, int value)
252 {
253   gtk_spin_button_set_value(&widget, value);
254 }
255 void IntSpinnerExport(GtkSpinButton& widget, const IntImportCallback& importCallback)
256 {
257   importCallback(gtk_spin_button_get_value_as_int(&widget));
258 }
259 typedef ImportExport<GtkSpinButton, int, IntSpinnerImport, IntSpinnerExport> IntSpinnerImportExport;
260
261
262 void IntAdjustmentImport(GtkAdjustment& widget, int value)
263 {
264   gtk_adjustment_set_value(&widget, value);
265 }
266 void IntAdjustmentExport(GtkAdjustment& widget, const IntImportCallback& importCallback)
267 {
268   importCallback((int)gtk_adjustment_get_value(&widget));
269 }
270 typedef ImportExport<GtkAdjustment, int, IntAdjustmentImport, IntAdjustmentExport> IntAdjustmentImportExport;
271
272
273 void IntComboImport(GtkComboBox& widget, int value)
274 {
275   gtk_combo_box_set_active(&widget, value);
276 }
277 void IntComboExport(GtkComboBox& widget, const IntImportCallback& importCallback)
278 {
279   importCallback(gtk_combo_box_get_active(&widget));
280 }
281 typedef ImportExport<GtkComboBox, int, IntComboImport, IntComboExport> IntComboImportExport;
282
283
284 template<typename FirstArgument>
285 class CallbackDialogData : public DLG_DATA
286 {
287 public:
288   typedef Callback1<FirstArgument> ImportCallback;
289   typedef Callback1<const ImportCallback&> ExportCallback;
290
291 private:
292   ImportCallback m_importWidget;
293   ExportCallback m_exportWidget;
294   ImportCallback m_importViewer;
295   ExportCallback m_exportViewer;
296
297 public:
298   CallbackDialogData(const ImportCallback& importWidget, const ExportCallback& exportWidget, const ImportCallback& importViewer, const ExportCallback& exportViewer)
299     : m_importWidget(importWidget), m_exportWidget(exportWidget), m_importViewer(importViewer), m_exportViewer(exportViewer)
300   {
301   }
302   void release()
303   {
304     delete this;
305   }
306   void importData() const
307   {
308     m_exportViewer(m_importWidget);
309   }
310   void exportData() const
311   {
312     m_exportWidget(m_importViewer);
313   }
314 };
315
316 template<typename Widget, typename Viewer>
317 class AddData
318 {
319   DialogDataList& m_data;
320 public:
321   AddData(DialogDataList& data) : m_data(data)
322   {
323   }
324   void apply(typename Widget::Type& widget, typename Viewer::Type& viewer) const
325   {
326     m_data.push_back(
327       new CallbackDialogData<typename Widget::Other>(
328         typename Widget::ImportCaller(widget),
329         typename Widget::ExportCaller(widget),
330         typename Viewer::ImportCaller(viewer),
331         typename Viewer::ExportCaller(viewer)
332       )
333     );
334   }
335 };
336
337 template<typename Widget>
338 class AddCustomData
339 {
340   DialogDataList& m_data;
341 public:
342   AddCustomData(DialogDataList& data) : m_data(data)
343   {
344   }
345   void apply(
346     typename Widget::Type& widget,
347     const Callback1<typename Widget::Other>& importViewer,
348     const Callback1<const Callback1<typename Widget::Other>&>& exportViewer
349   ) const
350   {
351     m_data.push_back(
352       new CallbackDialogData<typename Widget::Other>(
353         typename Widget::ImportCaller(widget),
354         typename Widget::ExportCaller(widget),
355         importViewer,
356         exportViewer
357       )
358     );
359   }
360 };
361
362 // =============================================================================
363 // Dialog class
364
365 Dialog::Dialog() : m_window(0), m_parent(0)
366 {
367 }
368
369 Dialog::~Dialog()
370 {
371   for(DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i)
372   {
373     (*i)->release();
374   }
375  
376   ASSERT_MESSAGE(m_window == 0, "dialog window not destroyed");
377 }
378
379 void Dialog::ShowDlg()
380 {
381   ASSERT_MESSAGE(m_window != 0, "dialog was not constructed");
382   importData();
383   gtk_widget_show(GTK_WIDGET(m_window));
384 }
385
386 void Dialog::HideDlg()
387 {
388   ASSERT_MESSAGE(m_window != 0, "dialog was not constructed");
389   exportData();
390   gtk_widget_hide(GTK_WIDGET(m_window));
391 }
392
393 static gint delete_event_callback(GtkWidget *widget, GdkEvent* event, gpointer data)
394 {
395   reinterpret_cast<Dialog*>(data)->HideDlg();
396   reinterpret_cast<Dialog*>(data)->EndModal(eIDCANCEL);
397   return TRUE;
398 }
399
400 void Dialog::Create()
401 {
402   ASSERT_MESSAGE(m_window == 0, "dialog cannot be constructed");
403
404   m_window = BuildDialog();
405   g_signal_connect(G_OBJECT(m_window), "delete_event", G_CALLBACK(delete_event_callback), this);
406 }
407
408 void Dialog::Destroy()
409 {
410   ASSERT_MESSAGE(m_window != 0, "dialog cannot be destroyed");
411
412   gtk_widget_destroy(GTK_WIDGET(m_window));
413   m_window = 0;
414 }
415
416
417 void Dialog::AddBoolToggleData(GtkToggleButton& widget, const BoolImportCallback& importViewer, const BoolExportCallback& exportViewer)
418 {
419   AddCustomData<BoolToggleImportExport>(m_data).apply(widget, importViewer, exportViewer);
420 }
421
422 void Dialog::AddIntRadioData(GtkRadioButton& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
423 {
424   AddCustomData<IntRadioImportExport>(m_data).apply(widget, importViewer, exportViewer);
425 }
426
427 void Dialog::AddTextEntryData(GtkEntry& widget, const StringImportCallback& importViewer, const StringExportCallback& exportViewer)
428 {
429   AddCustomData<TextEntryImportExport>(m_data).apply(widget, importViewer, exportViewer);
430 }
431
432 void Dialog::AddIntEntryData(GtkEntry& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
433 {
434   AddCustomData<IntEntryImportExport>(m_data).apply(widget, importViewer, exportViewer);
435 }
436
437 void Dialog::AddSizeEntryData(GtkEntry& widget, const SizeImportCallback& importViewer, const SizeExportCallback& exportViewer)
438 {
439   AddCustomData<SizeEntryImportExport>(m_data).apply(widget, importViewer, exportViewer);
440 }
441
442 void Dialog::AddFloatEntryData(GtkEntry& widget, const FloatImportCallback& importViewer, const FloatExportCallback& exportViewer)
443 {
444   AddCustomData<FloatEntryImportExport>(m_data).apply(widget, importViewer, exportViewer);
445 }
446
447 void Dialog::AddFloatSpinnerData(GtkSpinButton& widget, const FloatImportCallback& importViewer, const FloatExportCallback& exportViewer)
448 {
449   AddCustomData<FloatSpinnerImportExport>(m_data).apply(widget, importViewer, exportViewer);
450 }
451
452 void Dialog::AddIntSpinnerData(GtkSpinButton& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
453 {
454   AddCustomData<IntSpinnerImportExport>(m_data).apply(widget, importViewer, exportViewer);
455 }
456
457 void Dialog::AddIntAdjustmentData(GtkAdjustment& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
458 {
459   AddCustomData<IntAdjustmentImportExport>(m_data).apply(widget, importViewer, exportViewer);
460 }
461
462 void Dialog::AddIntComboData(GtkComboBox& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
463 {
464   AddCustomData<IntComboImportExport>(m_data).apply(widget, importViewer, exportViewer);
465 }
466
467
468 void Dialog::AddDialogData(GtkToggleButton& widget, bool& data)
469 {
470   AddData<BoolToggleImportExport, BoolImportExport>(m_data).apply(widget, data);
471 }
472 void Dialog::AddDialogData(GtkRadioButton& widget, int& data)
473 {
474   AddData<IntRadioImportExport, IntImportExport>(m_data).apply(widget, data);
475 }
476 void Dialog::AddDialogData(GtkEntry& widget, CopiedString& data)
477 {
478   AddData<TextEntryImportExport, StringImportExport>(m_data).apply(widget, data);
479 }
480 void Dialog::AddDialogData(GtkEntry& widget, int& data)
481 {
482   AddData<IntEntryImportExport, IntImportExport>(m_data).apply(widget, data);
483 }
484 void Dialog::AddDialogData(GtkEntry& widget, std::size_t& data)
485 {
486   AddData<SizeEntryImportExport, SizeImportExport>(m_data).apply(widget, data);
487 }
488 void Dialog::AddDialogData(GtkEntry& widget, float& data)
489 {
490   AddData<FloatEntryImportExport, FloatImportExport>(m_data).apply(widget, data);
491 }
492 void Dialog::AddDialogData(GtkSpinButton& widget, float& data)
493 {
494   AddData<FloatSpinnerImportExport, FloatImportExport>(m_data).apply(widget, data);
495 }
496 void Dialog::AddDialogData(GtkSpinButton& widget, int& data)
497 {
498   AddData<IntSpinnerImportExport, IntImportExport>(m_data).apply(widget, data);
499 }
500 void Dialog::AddDialogData(GtkAdjustment& widget, int& data)
501 {
502   AddData<IntAdjustmentImportExport, IntImportExport>(m_data).apply(widget, data);
503 }
504 void Dialog::AddDialogData(GtkComboBox& widget, int& data)
505 {
506   AddData<IntComboImportExport, IntImportExport>(m_data).apply(widget, data);
507 }
508
509 void Dialog::exportData()
510 {
511   for(DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i)
512   {
513     (*i)->exportData();
514   }
515 }
516
517 void Dialog::importData()
518 {
519   for(DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i)
520   {
521     (*i)->importData();
522   }
523 }
524
525 void Dialog::EndModal (EMessageBoxReturn code)
526 {
527   m_modal.loop = 0;
528   m_modal.ret = code;
529 }
530
531 EMessageBoxReturn Dialog::DoModal()
532 {
533   importData();
534
535   PreModal();
536
537   EMessageBoxReturn ret = modal_dialog_show(m_window, m_modal);
538   ASSERT_NOTNULL(m_window);
539   if(ret == eIDOK)
540   {
541     exportData();
542   }
543
544   gtk_widget_hide(GTK_WIDGET(m_window));
545
546   PostModal(m_modal.ret);
547
548   return m_modal.ret;
549 }
550
551
552 GtkWidget* Dialog::addCheckBox(GtkWidget* vbox, const char* name, const char* flag, const BoolImportCallback& importViewer, const BoolExportCallback& exportViewer)
553 {
554   GtkWidget* check = gtk_check_button_new_with_label(flag);
555   gtk_widget_show(check);
556   AddBoolToggleData(*GTK_TOGGLE_BUTTON(check), importViewer, exportViewer);
557
558   DialogVBox_packRow(GTK_VBOX(vbox), GTK_WIDGET(DialogRow_new(name, check)));
559   return check;
560 }
561
562 GtkWidget* Dialog::addCheckBox(GtkWidget* vbox, const char* name, const char* flag, bool& data)
563 {
564   return addCheckBox(vbox, name, flag, BoolImportCaller(data), BoolExportCaller(data));
565 }
566
567 void Dialog::addCombo(GtkWidget* vbox, const char* name, StringArrayRange values, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
568 {
569   GtkWidget* alignment = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
570   gtk_widget_show(alignment);
571   {
572     GtkWidget* combo = gtk_combo_box_new_text();
573
574     for(StringArrayRange::Iterator i = values.first; i != values.last; ++i)
575     {
576       gtk_combo_box_append_text(GTK_COMBO_BOX(combo), *i);
577     }
578
579     AddIntComboData(*GTK_COMBO_BOX(combo), importViewer, exportViewer);
580
581     gtk_widget_show (combo);
582     gtk_container_add(GTK_CONTAINER(alignment), combo);
583   }
584
585   GtkTable* row = DialogRow_new(name, alignment);
586   DialogVBox_packRow(GTK_VBOX(vbox), GTK_WIDGET(row));
587 }
588
589 void Dialog::addCombo(GtkWidget* vbox, const char* name, int& data, StringArrayRange values)
590 {
591   addCombo(vbox, name, values, IntImportCaller(data), IntExportCaller(data));
592 }
593
594 void Dialog::addSlider(GtkWidget* vbox, const char* name, int& data, gboolean draw_value, const char* low, const char* high, double value, double lower, double upper, double step_increment, double page_increment, double page_size)
595 {
596 #if 0
597   if(draw_value == FALSE)
598   {
599     GtkWidget* hbox2 = gtk_hbox_new (FALSE, 0);
600     gtk_widget_show (hbox2);
601     gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox2), FALSE, FALSE, 0);
602     {
603       GtkWidget* label = gtk_label_new (low);
604       gtk_widget_show (label);
605       gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
606     }
607     {
608       GtkWidget* label = gtk_label_new (high);
609       gtk_widget_show (label);
610       gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
611     }
612   }
613 #endif
614
615   // adjustment
616   GtkObject* adj = gtk_adjustment_new(value, lower, upper, step_increment, page_increment, page_size);
617   AddIntAdjustmentData(*GTK_ADJUSTMENT(adj), IntImportCaller(data), IntExportCaller(data));
618
619   // scale
620   GtkWidget* alignment = gtk_alignment_new(0.0, 0.5, 1.0, 0.0);
621   gtk_widget_show(alignment);
622
623   GtkWidget* scale = gtk_hscale_new(GTK_ADJUSTMENT(adj));
624   gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_LEFT);
625   gtk_widget_show(scale);
626   gtk_container_add(GTK_CONTAINER(alignment), scale);
627
628   gtk_scale_set_draw_value(GTK_SCALE (scale), draw_value);
629   gtk_scale_set_digits(GTK_SCALE (scale), 0);
630
631   GtkTable* row = DialogRow_new(name, alignment);
632   DialogVBox_packRow(GTK_VBOX(vbox), GTK_WIDGET(row));
633 }
634   
635 void Dialog::addRadio(GtkWidget* vbox, const char* name, StringArrayRange names, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
636 {
637   GtkWidget* alignment = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
638   gtk_widget_show(alignment);
639   {
640     RadioHBox radioBox = RadioHBox_new(names);
641     gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(radioBox.m_hbox));
642     AddIntRadioData(*GTK_RADIO_BUTTON(radioBox.m_radio), importViewer, exportViewer);
643   }
644
645   GtkTable* row = DialogRow_new(name, alignment);
646   DialogVBox_packRow(GTK_VBOX(vbox), GTK_WIDGET(row));
647 }
648
649 void Dialog::addRadio(GtkWidget* vbox, const char* name, int& data, StringArrayRange names)
650 {
651   addRadio(vbox, name, names, IntImportCaller(data), IntExportCaller(data));
652 }
653
654 void Dialog::addRadioIcons(GtkWidget* vbox, const char* name, StringArrayRange icons, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
655 {
656   GtkWidget* table = gtk_table_new (2, static_cast<guint>(icons.last - icons.first), FALSE);
657   gtk_widget_show (table);
658
659   gtk_table_set_row_spacings (GTK_TABLE (table), 5);
660   gtk_table_set_col_spacings (GTK_TABLE (table), 5);
661
662   GSList* group = 0;
663   GtkWidget* radio = 0;
664   for(StringArrayRange::Iterator icon = icons.first; icon != icons.last; ++icon)
665   {
666     guint pos = static_cast<guint>(icon - icons.first);
667     GtkImage* image = new_local_image(*icon);
668     gtk_widget_show(GTK_WIDGET(image));
669     gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(image), pos, pos+1, 0, 1,
670                       (GtkAttachOptions) (0),
671                       (GtkAttachOptions) (0), 0, 0);
672
673     radio = gtk_radio_button_new(group);
674     gtk_widget_show (radio);
675     gtk_table_attach (GTK_TABLE (table), radio, pos, pos+1, 1, 2,
676                       (GtkAttachOptions) (0),
677                       (GtkAttachOptions) (0), 0, 0);
678
679     group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
680   }
681
682   AddIntRadioData(*GTK_RADIO_BUTTON(radio), importViewer, exportViewer);
683
684   DialogVBox_packRow(GTK_VBOX(vbox), GTK_WIDGET(DialogRow_new(name, table)));
685 }
686
687 void Dialog::addRadioIcons(GtkWidget* vbox, const char* name, int& data, StringArrayRange icons)
688 {
689   addRadioIcons(vbox, name, icons, IntImportCaller(data), IntExportCaller(data));
690 }
691
692 GtkWidget* Dialog::addIntEntry(GtkWidget* vbox, const char* name, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
693 {
694   DialogEntryRow row(DialogEntryRow_new(name));
695   AddIntEntryData(*row.m_entry, importViewer, exportViewer);
696   DialogVBox_packRow(GTK_VBOX(vbox), row.m_row);
697   return row.m_row;
698 }
699
700 GtkWidget* Dialog::addSizeEntry(GtkWidget* vbox, const char* name, const SizeImportCallback& importViewer, const SizeExportCallback& exportViewer)
701 {
702   DialogEntryRow row(DialogEntryRow_new(name));
703   AddSizeEntryData(*row.m_entry, importViewer, exportViewer);
704   DialogVBox_packRow(GTK_VBOX(vbox), row.m_row);
705   return row.m_row;
706 }
707
708 GtkWidget* Dialog::addFloatEntry(GtkWidget* vbox, const char* name, const FloatImportCallback& importViewer, const FloatExportCallback& exportViewer)
709 {
710   DialogEntryRow row(DialogEntryRow_new(name));
711   AddFloatEntryData(*row.m_entry, importViewer, exportViewer);
712   DialogVBox_packRow(GTK_VBOX(vbox), row.m_row);
713   return row.m_row;
714 }
715
716 GtkWidget* Dialog::addPathEntry(GtkWidget* vbox, const char* name, bool browse_directory, const StringImportCallback& importViewer, const StringExportCallback& exportViewer)
717 {
718   PathEntry pathEntry = PathEntry_new();
719   g_signal_connect(G_OBJECT(pathEntry.m_button), "clicked", G_CALLBACK(browse_directory ? button_clicked_entry_browse_directory : button_clicked_entry_browse_file), pathEntry.m_entry);
720
721   AddTextEntryData(*GTK_ENTRY(pathEntry.m_entry), importViewer, exportViewer);
722
723   GtkTable* row = DialogRow_new(name, GTK_WIDGET(pathEntry.m_frame));
724   DialogVBox_packRow(GTK_VBOX(vbox), GTK_WIDGET(row));
725
726   return GTK_WIDGET(row);
727 }
728
729 GtkWidget* Dialog::addPathEntry(GtkWidget* vbox, const char* name, CopiedString& data, bool browse_directory)
730 {
731   return addPathEntry(vbox, name, browse_directory, StringImportCallback(StringImportCaller(data)), StringExportCallback(StringExportCaller(data))); 
732 }
733
734 GtkWidget* Dialog::addSpinner(GtkWidget* vbox, const char* name, double value, double lower, double upper, const IntImportCallback& importViewer, const IntExportCallback& exportViewer)
735 {
736   DialogSpinnerRow row(DialogSpinnerRow_new(name, value, lower, upper, 1));
737   AddIntSpinnerData(*row.m_spin, importViewer, exportViewer);
738   DialogVBox_packRow(GTK_VBOX(vbox), row.m_row);
739   return row.m_row;
740 }
741
742 GtkWidget* Dialog::addSpinner(GtkWidget* vbox, const char* name, int& data, double value, double lower, double upper)
743 {
744   return addSpinner(vbox, name, value, lower, upper, IntImportCallback(IntImportCaller(data)), IntExportCallback(IntExportCaller(data)));
745 }
746
747 GtkWidget* Dialog::addSpinner(GtkWidget* vbox, const char* name, double value, double lower, double upper, const FloatImportCallback& importViewer, const FloatExportCallback& exportViewer)
748 {
749   DialogSpinnerRow row(DialogSpinnerRow_new(name, value, lower, upper, 10));
750   AddFloatSpinnerData(*row.m_spin, importViewer, exportViewer);
751   DialogVBox_packRow(GTK_VBOX(vbox), row.m_row);
752   return row.m_row;
753 }