]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/camera/dialogs.cpp
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / contrib / camera / dialogs.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    Camera plugin for GtkRadiant
24    Copyright (C) 2002 Splash Damage Ltd.
25  */
26
27 #include "camera.h"
28
29 static GSList *g_pEditTypeRadio = NULL;
30 static GtkWidget *g_pEditModeEditRadioButton = NULL;
31 GtkWidget *g_pEditModeAddRadioButton = NULL;
32 static GtkWidget *g_pSecondsEntry = NULL;
33 static GtkWidget *g_pEventsList = NULL;
34 static GtkLabel *g_pCurrentTime = NULL;
35 static GtkLabel *g_pTotalTime = NULL;
36 static GtkAdjustment *g_pTimeLine = NULL;
37 static GtkWidget *g_pTrackCamera = NULL;
38 static GtkWidget *g_pCamName = NULL;
39 static char *g_cNull = '\0';
40
41 static gint ci_editmode_edit(GtkWidget *widget, gpointer data)
42 {
43     g_iEditMode = 0;
44
45     return TRUE;
46 }
47
48 static gint ci_editmode_add(GtkWidget *widget, gpointer data)
49 {
50     g_iEditMode = 1;
51
52     return TRUE;
53 }
54
55 /*static gint ci_delete_selected( GtkWidget *widget, gpointer data )
56    {
57    return TRUE;
58    }
59
60    static gint ci_select_all( GtkWidget *widget, gpointer data )
61    {
62    return TRUE;
63    }*/
64
65 static gint ci_new(GtkWidget *widget, gpointer data)
66 {
67     GtkWidget *w, *hbox; //, *name;
68     GtkWidget *fixed, *interpolated, *spline;
69     EMessageBoxReturn ret;
70     int loop = 1;
71     GSList *targetTypeRadio = NULL;
72 //      char buf[128];
73
74     // create the window
75     auto window = ui::Window(ui::window_type::TOP);
76     gtk_window_set_title(window, "New Camera");
77     window.connect("delete_event", G_CALLBACK(dialog_delete_callback), NULL);
78     window.connect("destroy", G_CALLBACK(gtk_widget_destroy), NULL);
79     gtk_window_set_transient_for(window, g_pCameraInspectorWnd);
80
81     g_object_set_data(G_OBJECT(window), "loop", &loop);
82     g_object_set_data(G_OBJECT(window), "ret", &ret);
83
84     gtk_widget_realize(window);
85
86     // fill the window
87     auto vbox = ui::VBox(FALSE, 5);
88     window.add(vbox);
89     vbox.show();
90
91     // -------------------------- //
92
93     hbox = ui::HBox(FALSE, 5);
94     vbox.pack_start(hbox, FALSE, FALSE, 0);
95     hbox.show();
96
97     auto frame = ui::Frame("Type");
98     hbox.pack_start(frame, TRUE, TRUE, 0);
99     frame.show();
100
101     auto vbox2 = ui::VBox(FALSE, 5);
102     frame.add(vbox2);
103     gtk_container_set_border_width(GTK_CONTAINER(vbox2), 5);
104     vbox2.show();
105
106     // -------------------------- //
107
108     fixed = gtk_radio_button_new_with_label(targetTypeRadio, "Fixed");
109     vbox2.pack_start(fixed, FALSE, FALSE, 3);
110     fixed.show();
111     targetTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(fixed));
112
113     interpolated = gtk_radio_button_new_with_label(targetTypeRadio, "Interpolated");
114     vbox2.pack_start(interpolated, FALSE, FALSE, 3);
115     interpolated.show();
116     targetTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(interpolated));
117
118     spline = gtk_radio_button_new_with_label(targetTypeRadio, "Spline");
119     vbox2.pack_start(spline, FALSE, FALSE, 3);
120     spline.show();
121     targetTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(spline));
122
123     // -------------------------- //
124
125     w = gtk_hseparator_new();
126     vbox.pack_start(w, FALSE, FALSE, 2);
127     w.show();
128
129     // -------------------------- //
130
131     hbox = ui::HBox(FALSE, 5);
132     vbox.pack_start(hbox, FALSE, FALSE, 0);
133     hbox.show();
134
135     w = ui::Button("Ok");
136     hbox.pack_start(w, TRUE, TRUE, 0);
137     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDOK));
138     w.show();
139
140     gtk_widget_set_can_default(w, true);
141     gtk_widget_grab_default(w);
142
143     w = ui::Button("Cancel");
144     hbox.pack_start(w, TRUE, TRUE, 0);
145     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDCANCEL));
146     w.show();
147     ret = eIDCANCEL;
148
149     // -------------------------- //
150
151     gtk_window_set_position(window, GTK_WIN_POS_CENTER);
152     window.show();
153     gtk_grab_add(window);
154
155     bool dialogError = TRUE;
156     while (dialogError) {
157         loop = 1;
158         while (loop) {
159             gtk_main_iteration();
160         }
161
162         dialogError = FALSE;
163
164         if (ret == eIDOK) {
165             if (gtk_toggle_button_get_active((GtkToggleButton *) fixed)) {
166                 DoNewFixedCamera();
167             } else if (gtk_toggle_button_get_active((GtkToggleButton *) interpolated)) {
168                 DoNewInterpolatedCamera();
169             } else if (gtk_toggle_button_get_active((GtkToggleButton *) spline)) {
170                 DoNewSplineCamera();
171             }
172         }
173     }
174
175     gtk_grab_remove(window);
176     window.destroy();
177
178     return TRUE;
179 }
180
181 static gint ci_load(GtkWidget *widget, gpointer data)
182 {
183     DoLoadCamera();
184
185     return TRUE;
186 }
187
188 static gint ci_save(GtkWidget *widget, gpointer data)
189 {
190     DoSaveCamera();
191
192     return TRUE;
193 }
194
195 static gint ci_unload(GtkWidget *widget, gpointer data)
196 {
197     DoUnloadCamera();
198
199     return TRUE;
200 }
201
202 static gint ci_apply(GtkWidget *widget, gpointer data)
203 {
204     if (GetCurrentCam()) {
205         const char *str;
206         char buf[128];
207         bool build = false;
208
209         str = gtk_entry_get_text(GTK_ENTRY(g_pCamName));
210
211         if (str) {
212             GetCurrentCam()->GetCam()->setName(str);
213             build = true;
214         }
215
216         str = gtk_entry_get_text(GTK_ENTRY(g_pSecondsEntry));
217
218         if (str) {
219             GetCurrentCam()->GetCam()->setBaseTime(atof(str));
220             build = true;
221         }
222
223         if (build) {
224             GetCurrentCam()->GetCam()->buildCamera();
225         }
226
227         sprintf(buf, "%.2f", GetCurrentCam()->GetCam()->getBaseTime());
228         gtk_entry_set_text(GTK_ENTRY(g_pSecondsEntry), buf);
229
230         sprintf(buf, "%.2f", GetCurrentCam()->GetCam()->getTotalTime());
231         gtk_label_set_text(g_pCurrentTime, "0.00");
232         gtk_label_set_text(g_pTotalTime, buf);
233
234         gtk_adjustment_set_value(g_pTimeLine, 0.f);
235         g_pTimeLine->upper = GetCurrentCam()->GetCam()->getTotalTime() * 1000;
236
237         GetCurrentCam()->HasBeenModified();
238     }
239
240     return TRUE;
241 }
242
243 static gint ci_preview(GtkWidget *widget, gpointer data)
244 {
245     if (GetCurrentCam()) {
246         g_iPreviewRunning = 1;
247         g_FuncTable.m_pfnSysUpdateWindows(W_XY_OVERLAY | W_CAMERA);
248     }
249
250     return TRUE;
251 }
252
253 static gint ci_expose(GtkWidget *widget, gpointer data)
254 {
255     // start edit mode
256     DoStartEdit(GetCurrentCam());
257
258     return FALSE;
259 }
260
261 static gint ci_close(GtkWidget *widget, gpointer data)
262 {
263     gtk_widget_hide(g_pCameraInspectorWnd);
264
265     // exit edit mode
266     DoStopEdit();
267
268     return TRUE;
269 }
270
271 static GtkWidget *g_pPathListCombo = NULL;
272 static GtkLabel *g_pPathType = NULL;
273
274 static void RefreshPathListCombo(void)
275 {
276     if (!g_pPathListCombo) {
277         return;
278     }
279
280     GList *combo_list = (GList *) NULL;
281
282     if (GetCurrentCam()) {
283         combo_list = g_list_append(combo_list, (void *) GetCurrentCam()->GetCam()->getPositionObj()->getName());
284         for (int i = 0; i < GetCurrentCam()->GetCam()->numTargets(); i++) {
285             combo_list = g_list_append(combo_list, (void *) GetCurrentCam()->GetCam()->getActiveTarget(i)->getName());
286         }
287     } else {
288         // add one empty string make gtk be quiet
289         combo_list = g_list_append(combo_list, (gpointer) g_cNull);
290     }
291
292     gtk_combo_set_popdown_strings(GTK_COMBO(g_pPathListCombo), combo_list);
293     g_list_free(combo_list);
294 }
295
296 static gint ci_pathlist_changed(GtkWidget *widget, gpointer data)
297 {
298     const char *str = gtk_entry_get_text(GTK_ENTRY(widget));
299
300     if (!str || !GetCurrentCam()) {
301         return TRUE;
302     }
303
304     int i;
305     for (i = 0; i < GetCurrentCam()->GetCam()->numTargets(); i++) {
306         if (!strcmp(str, GetCurrentCam()->GetCam()->getActiveTarget(i)->getName())) {
307             break;
308         }
309     }
310
311     if (i >= 0 && i < GetCurrentCam()->GetCam()->numTargets()) {
312         GetCurrentCam()->GetCam()->setActiveTarget(i);
313
314         g_iActiveTarget = i;
315         if (g_pPathType) {
316             gtk_label_set_text(g_pPathType, GetCurrentCam()->GetCam()->getActiveTarget(g_iActiveTarget)->typeStr());
317         }
318     } else {
319         g_iActiveTarget = -1;
320         if (g_pPathType) {
321             gtk_label_set_text(g_pPathType, GetCurrentCam()->GetCam()->getPositionObj()->typeStr());
322         }
323     }
324
325     // start edit mode
326     if (g_pCameraInspectorWnd && gtk_widget_get_visible(g_pCameraInspectorWnd)) {
327         DoStartEdit(GetCurrentCam());
328     }
329
330     return TRUE;
331 }
332
333 static void RefreshEventList(void)
334 {
335     int i;
336     char buf[128];
337
338     // Clear events list
339     gtk_clist_freeze(GTK_CLIST(g_pEventsList));
340     gtk_clist_clear(GTK_CLIST(g_pEventsList));
341
342     if (GetCurrentCam()) {
343         // Fill events list
344         for (i = 0; i < GetCurrentCam()->GetCam()->numEvents(); i++) {
345             char rowbuf[3][128], *row[3];
346             // FIXME: sort by time?
347             sprintf(rowbuf[0], "%li", GetCurrentCam()->GetCam()->getEvent(i)->getTime());
348             row[0] = rowbuf[0];
349             strncpy(rowbuf[1], GetCurrentCam()->GetCam()->getEvent(i)->typeStr(), sizeof(rowbuf[0]));
350             row[1] = rowbuf[1];
351             strncpy(rowbuf[2], GetCurrentCam()->GetCam()->getEvent(i)->getParam(), sizeof(rowbuf[1]));
352             row[2] = rowbuf[2];
353             gtk_clist_append(GTK_CLIST(g_pEventsList), row);
354         }
355
356         // Total duration might have changed
357         sprintf(buf, "%.2f", GetCurrentCam()->GetCam()->getTotalTime());
358         gtk_label_set_text(g_pCurrentTime, "0.00");
359         gtk_label_set_text(g_pTotalTime, buf);
360
361         gtk_adjustment_set_value(g_pTimeLine, 0.f);
362         g_pTimeLine->upper = (GetCurrentCam()->GetCam()->getTotalTime() * 1000);
363     }
364
365     gtk_clist_thaw(GTK_CLIST(g_pEventsList));
366 }
367
368 static gint ci_rename(GtkWidget *widget, gpointer data)
369 {
370     GtkWidget *w, *hbox, *name;
371     EMessageBoxReturn ret;
372     int loop = 1;
373
374     if (!GetCurrentCam()) {
375         return TRUE;
376     }
377
378     // create the window
379     auto window = ui::Window(ui::window_type::TOP);
380     gtk_window_set_title(window, "Rename Path");
381     window.connect("delete_event", G_CALLBACK(dialog_delete_callback), NULL);
382     window.connect("destroy", G_CALLBACK(gtk_widget_destroy), NULL);
383     gtk_window_set_transient_for(window, g_pCameraInspectorWnd);
384
385     g_object_set_data(G_OBJECT(window), "loop", &loop);
386     g_object_set_data(G_OBJECT(window), "ret", &ret);
387
388     gtk_widget_realize(window);
389
390     // fill the window
391     auto vbox = ui::VBox(FALSE, 5);
392     window.add(vbox);
393     vbox.show();
394
395     // -------------------------- //
396
397     hbox = ui::HBox(FALSE, 5);
398     vbox.pack_start(hbox, FALSE, FALSE, 0);
399     hbox.show();
400
401     w = ui::Label("Name:");
402     hbox.pack_start(w, FALSE, FALSE, 0);
403     w.show();
404
405     name = ui::Entry();
406     hbox.pack_start(name, FALSE, FALSE, 0);
407     name.show();
408
409     if (g_iActiveTarget < 0) {
410         gtk_entry_set_text(GTK_ENTRY(name), GetCurrentCam()->GetCam()->getPositionObj()->getName());
411     } else {
412         gtk_entry_set_text(GTK_ENTRY(name), GetCurrentCam()->GetCam()->getActiveTarget(g_iActiveTarget)->getName());
413     }
414
415     // -------------------------- //
416
417     w = gtk_hseparator_new();
418     vbox.pack_start(w, FALSE, FALSE, 2);
419     w.show();
420
421     // -------------------------- //
422
423     hbox = ui::HBox(FALSE, 5);
424     vbox.pack_start(hbox, FALSE, FALSE, 0);
425     hbox.show();
426
427     w = ui::Button("Ok");
428     hbox.pack_start(w, TRUE, TRUE, 0);
429     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDOK));
430     w.show();
431
432     gtk_widget_set_can_default(w, true);
433     gtk_widget_grab_default(w);
434
435     w = ui::Button("Cancel");
436     hbox.pack_start(w, TRUE, TRUE, 0);
437     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDCANCEL));
438     w.show();
439     ret = eIDCANCEL;
440
441     // -------------------------- //
442
443     gtk_window_set_position(window, GTK_WIN_POS_CENTER);
444     window.show();
445     gtk_grab_add(window);
446
447     bool dialogError = TRUE;
448     while (dialogError) {
449         loop = 1;
450         while (loop) {
451             gtk_main_iteration();
452         }
453
454         dialogError = FALSE;
455
456         if (ret == eIDOK) {
457             const char *str = gtk_entry_get_text(GTK_ENTRY(name));
458
459             if (str && str[0]) {
460                 // Update the path
461                 if (g_iActiveTarget < 0) {
462                     GetCurrentCam()->GetCam()->getPositionObj()->setName(str);
463                 } else {
464                     GetCurrentCam()->GetCam()->getActiveTarget(g_iActiveTarget)->setName(str);
465                 }
466
467                 GetCurrentCam()->GetCam()->buildCamera();
468
469                 // Rebuild the listbox
470                 RefreshPathListCombo();
471             } else {
472                 dialogError = TRUE;
473             }
474         }
475     }
476
477     gtk_grab_remove(window);
478     window.destroy();
479
480     return TRUE;
481 }
482
483 static gint ci_add_target(GtkWidget *widget, gpointer data)
484 {
485     GtkWidget *w, *hbox, *name;
486     GtkWidget *fixed, *interpolated, *spline;
487     EMessageBoxReturn ret;
488     int loop = 1;
489     GSList *targetTypeRadio = NULL;
490     char buf[128];
491
492     if (!GetCurrentCam()) {
493         return TRUE;
494     }
495
496     // create the window
497     auto window = ui::Window(ui::window_type::TOP);
498     gtk_window_set_title(window, "Add Target");
499     window.connect("delete_event", G_CALLBACK(dialog_delete_callback), NULL);
500     window.connect("destroy", G_CALLBACK(gtk_widget_destroy), NULL);
501     gtk_window_set_transient_for(window, g_pCameraInspectorWnd);
502
503     g_object_set_data(G_OBJECT(window), "loop", &loop);
504     g_object_set_data(G_OBJECT(window), "ret", &ret);
505
506     gtk_widget_realize(window);
507
508     // fill the window
509     auto vbox = ui::VBox(FALSE, 5);
510     window.add(vbox);
511     vbox.show();
512
513     // -------------------------- //
514
515     hbox = ui::HBox(FALSE, 5);
516     vbox.pack_start(hbox, FALSE, FALSE, 0);
517     hbox.show();
518
519     w = ui::Label("Name:");
520     hbox.pack_start(w, FALSE, FALSE, 0);
521     w.show();
522
523     name = ui::Entry();
524     hbox.pack_start(name, TRUE, TRUE, 0);
525     name.show();
526
527     sprintf(buf, "target%i", GetCurrentCam()->GetCam()->numTargets() + 1);
528     gtk_entry_set_text(GTK_ENTRY(name), buf);
529
530     // -------------------------- //
531
532     hbox = ui::HBox(FALSE, 5);
533     vbox.pack_start(hbox, FALSE, FALSE, 0);
534     hbox.show();
535
536     auto frame = ui::Frame("Type");
537     hbox.pack_start(frame, TRUE, TRUE, 0);
538     frame.show();
539
540     auto vbox2 = ui::VBox(FALSE, 5);
541     frame.add(vbox2);
542     gtk_container_set_border_width(GTK_CONTAINER(vbox2), 5);
543     vbox2.show();
544
545     // -------------------------- //
546
547     fixed = gtk_radio_button_new_with_label(targetTypeRadio, "Fixed");
548     vbox2.pack_start(fixed, FALSE, FALSE, 3);
549     fixed.show();
550     targetTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(fixed));
551
552     interpolated = gtk_radio_button_new_with_label(targetTypeRadio, "Interpolated");
553     vbox2.pack_start(interpolated, FALSE, FALSE, 3);
554     interpolated.show();
555     targetTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(interpolated));
556
557     spline = gtk_radio_button_new_with_label(targetTypeRadio, "Spline");
558     vbox2.pack_start(spline, FALSE, FALSE, 3);
559     spline.show();
560     targetTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(spline));
561
562     // -------------------------- //
563
564     w = gtk_hseparator_new();
565     vbox.pack_start(w, FALSE, FALSE, 2);
566     w.show();
567
568     // -------------------------- //
569
570     hbox = ui::HBox(FALSE, 5);
571     vbox.pack_start(hbox, FALSE, FALSE, 0);
572     hbox.show();
573
574     w = ui::Button("Ok");
575     hbox.pack_start(w, TRUE, TRUE, 0);
576     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDOK));
577     w.show();
578
579     gtk_widget_set_can_default(w, true);
580     gtk_widget_grab_default(w);
581
582     w = ui::Button("Cancel");
583     hbox.pack_start(w, TRUE, TRUE, 0);
584     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDCANCEL));
585     w.show();
586     ret = eIDCANCEL;
587
588     // -------------------------- //
589
590     gtk_window_set_position(window, GTK_WIN_POS_CENTER);
591     window.show();
592     gtk_grab_add(window);
593
594     bool dialogError = TRUE;
595     while (dialogError) {
596         loop = 1;
597         while (loop) {
598             gtk_main_iteration();
599         }
600
601         dialogError = FALSE;
602
603         if (ret == eIDOK) {
604             const char *str = gtk_entry_get_text(GTK_ENTRY(name));
605
606             if (str && str[0]) {
607                 int type;
608                 GList *li;
609
610                 if (gtk_toggle_button_get_active((GtkToggleButton *) fixed)) {
611                     type = 0;
612                 } else if (gtk_toggle_button_get_active((GtkToggleButton *) interpolated)) {
613                     type = 1;
614                 } else if (gtk_toggle_button_get_active((GtkToggleButton *) spline)) {
615                     type = 2;
616                 }
617
618                 // Add the target
619                 GetCurrentCam()->GetCam()->addTarget(str, static_cast<idCameraPosition::positionType>( type ));
620
621                 // Rebuild the listbox
622                 RefreshPathListCombo();
623
624                 // Select the last item in the listbox
625                 li = g_list_last(GTK_LIST(GTK_COMBO(g_pPathListCombo)->list)->children);
626                 gtk_list_select_child(GTK_LIST(GTK_COMBO(g_pPathListCombo)->list), li->data);
627
628                 // If this was the first one, refresh the event list
629                 if (GetCurrentCam()->GetCam()->numTargets() == 1) {
630                     RefreshEventList();
631                 }
632
633                 // Go to editmode Add
634                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_pEditModeAddRadioButton), TRUE);
635
636             } else {
637                 dialogError = TRUE;
638             }
639         }
640     }
641
642     gtk_grab_remove(window);
643     window.destroy();
644
645     return TRUE;
646 }
647
648 static GtkWidget *g_pCamListCombo = NULL;
649 static GtkLabel *g_pCamType = NULL;
650
651 void RefreshCamListCombo(void)
652 {
653     if (!g_pCamListCombo) {
654         return;
655     }
656
657     GList *combo_list = (GList *) NULL;
658     CCamera *combo_cam = firstCam;
659     if (combo_cam) {
660         while (combo_cam) {
661             //combo_list = g_list_append( combo_list, (void *)combo_cam->GetCam()->getName() );
662             //if( combo_cam->HasBeenSaved() ) {
663             combo_list = g_list_append(combo_list, (void *) combo_cam->GetFileName());
664             /*} else {
665                 char buf[128];
666                 sprintf( buf, "Unsaved Camera %i", combo_cam->GetCamNum() );
667                 combo_list = g_list_append( combo_list, (void *)buf );
668
669                 //combo_list = g_list_append( combo_list, (void *)combo_cam->GetCam()->getName() );     // FIXME: this requires camera.dll to create unique names for new cams
670                }*/
671             combo_cam = combo_cam->GetNext();
672         }
673     } else {
674         // add one empty string make gtk be quiet
675         combo_list = g_list_append(combo_list, (gpointer) g_cNull);
676     }
677     gtk_combo_set_popdown_strings(GTK_COMBO(g_pCamListCombo), combo_list);
678     g_list_free(combo_list);
679
680     // select our current entry in the list
681     if (GetCurrentCam()) {
682         // stop editing on the current cam
683         //GetCurrentCam()->GetCam()->stopEdit();        // FIXME: this crashed on creating new cameras, why is it here?
684
685         GList *li = GTK_LIST(GTK_COMBO(g_pCamListCombo)->list)->children;
686         combo_cam = firstCam;
687         while (li && combo_cam) {
688             if (combo_cam == GetCurrentCam()) {
689                 gtk_list_select_child(GTK_LIST(GTK_COMBO(g_pCamListCombo)->list), li->data);
690                 break;
691             }
692             li = li->next;
693             combo_cam = combo_cam->GetNext();
694         }
695     }
696
697     RefreshPathListCombo();
698 }
699
700 static gint ci_camlist_changed(GtkWidget *widget, gpointer data)
701 {
702     const char *str = gtk_entry_get_text(GTK_ENTRY(widget));
703
704     CCamera *combo_cam = firstCam;
705     while (str && combo_cam) {
706         //if( !strcmp( str, combo_cam->GetCam()->getName() ) )
707         //if( combo_cam->HasBeenSaved() ) {
708         if (!strcmp(str, combo_cam->GetFileName())) {
709             break;
710         }
711         /*} else {
712             char buf[128];
713             sprintf( buf, "Unsaved Camera %i", combo_cam->GetCamNum() );
714             if( !strcmp( str, buf ) )
715             //if( !strcmp( str, combo_cam->GetCam()->getName() ) )
716                 break;
717            }*/
718
719         combo_cam = combo_cam->GetNext();
720     }
721
722     SetCurrentCam(combo_cam);
723
724     if (g_pCamType) {
725         if (GetCurrentCam()) {
726             // Fill in our widgets fields for this camera
727             char buf[128];
728
729             // Set Name
730             gtk_entry_set_text(GTK_ENTRY(g_pCamName), GetCurrentCam()->GetCam()->getName());
731
732             // Set type
733             gtk_label_set_text(g_pCamType, GetCurrentCam()->GetCam()->getPositionObj()->typeStr());
734
735             // Set duration
736             sprintf(buf, "%.2f", GetCurrentCam()->GetCam()->getBaseTime());
737             gtk_entry_set_text(GTK_ENTRY(g_pSecondsEntry), buf);
738
739             sprintf(buf, "%.2f", GetCurrentCam()->GetCam()->getTotalTime());
740             gtk_label_set_text(g_pCurrentTime, "0.00");
741             gtk_label_set_text(g_pTotalTime, buf);
742
743             gtk_adjustment_set_value(g_pTimeLine, 0.f);
744             g_pTimeLine->upper = GetCurrentCam()->GetCam()->getTotalTime() * 1000;
745         } else {
746             // Set Name
747             gtk_entry_set_text(GTK_ENTRY(g_pCamName), "");
748
749             // Set type
750             gtk_label_set_text(g_pCamType, "");
751
752             // Set duration
753             gtk_entry_set_text(GTK_ENTRY(g_pSecondsEntry), "30.00");
754
755             gtk_label_set_text(g_pCurrentTime, "0.00");
756             gtk_label_set_text(g_pTotalTime, "30.00");
757
758             gtk_adjustment_set_value(g_pTimeLine, 0.f);
759             g_pTimeLine->upper = 30000;
760         }
761
762         // Refresh event list
763         RefreshEventList();
764     }
765
766     RefreshPathListCombo();
767
768     // start edit mode
769     g_iActiveTarget = -1;
770     if (g_pCameraInspectorWnd && gtk_widget_get_visible(g_pCameraInspectorWnd)) {
771         DoStartEdit(GetCurrentCam());
772     }
773
774     return TRUE;
775 }
776
777 enum camEventType {
778     EVENT_NA = 0x00,
779     EVENT_WAIT,             //
780     EVENT_TARGETWAIT,   //
781     EVENT_SPEED,            //
782     EVENT_TARGET,           // char(name)
783     EVENT_SNAPTARGET,   //
784     EVENT_FOV,              // int(time), int(targetfov)
785     EVENT_CMD,              //
786     EVENT_TRIGGER,      //
787     EVENT_STOP,             //
788     EVENT_CAMERA,           //
789     EVENT_FADEOUT,      // int(time)
790     EVENT_FADEIN,           // int(time)
791     EVENT_FEATHER,      //
792     EVENT_COUNT
793 };
794
795 // { requires parameters, enabled }
796 const bool camEventFlags[][2] = {
797         {false, false},
798         {false, true},
799         {false, false},
800         {false, false},
801         {true,  true},
802         {false, false},
803         {true,  true},
804         {false, false},
805         {false, false},
806         {false, true},
807         {true,  true},
808         {true,  true},
809         {true,  true},
810         {false, true},
811 };
812
813 const char *camEventStr[] = {
814         "n/a",
815         "Wait",
816         "Target wait",
817         "Speed",
818         "Change Target <string:name>",
819         "Snap Target",
820         "FOV <int:duration> <int:targetfov>",
821         "Run Script",
822         "Trigger",
823         "Stop",
824         "Change to Camera <string:camera> (or <int:cameranum> <string:camera>",
825         "Fade Out <int:duration>",
826         "Fade In <int:duration>",
827         "Feather"
828 };
829
830 static gint ci_add(GtkWidget *widget, gpointer data)
831 {
832     GtkWidget *w, *hbox, *parameters;
833     GtkWidget *eventWidget[EVENT_COUNT];
834     EMessageBoxReturn ret;
835     int i, loop = 1;
836     GSList *eventTypeRadio = NULL;
837 //      char buf[128];
838
839     if (!GetCurrentCam()) {
840         return TRUE;
841     }
842
843     // create the window
844     auto window = ui::Window(ui::window_type::TOP);
845     gtk_window_set_title(window, "Add Event");
846     window.connect("delete_event", G_CALLBACK(dialog_delete_callback), NULL);
847     window.connect("destroy", G_CALLBACK(gtk_widget_destroy), NULL);
848     gtk_window_set_transient_for(window, g_pCameraInspectorWnd);
849
850     g_object_set_data(G_OBJECT(window), "loop", &loop);
851     g_object_set_data(G_OBJECT(window), "ret", &ret);
852
853     gtk_widget_realize(window);
854
855     // fill the window
856     auto vbox = ui::VBox(FALSE, 5);
857     window.add(vbox);
858     vbox.show();
859
860     // -------------------------- //
861
862     hbox = ui::HBox(FALSE, 5);
863     vbox.pack_start(hbox, FALSE, FALSE, 0);
864     hbox.show();
865
866     auto frame = ui::Frame("Type");
867     hbox.pack_start(frame, TRUE, TRUE, 0);
868     frame.show();
869
870     auto vbox2 = ui::VBox(FALSE, 5);
871     frame.add(vbox2);
872     gtk_container_set_border_width(GTK_CONTAINER(vbox2), 5);
873     vbox2.show();
874
875     // -------------------------- //
876
877     for (i = 1; i < EVENT_COUNT; i++) {
878         eventWidget[i] = gtk_radio_button_new_with_label(eventTypeRadio, camEventStr[i]);
879         vbox2.pack_start(eventWidget[i], FALSE, FALSE, 3);
880         eventWidget[i].show();
881         eventTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(eventWidget[i]));
882         if (camEventFlags[i][1] == false) {
883             gtk_widget_set_sensitive(eventWidget[i], FALSE);
884         }
885     }
886
887     // -------------------------- //
888
889     hbox = ui::HBox(FALSE, 5);
890     vbox.pack_start(hbox, FALSE, FALSE, 0);
891     hbox.show();
892
893     w = ui::Label("Parameters:");
894     hbox.pack_start(w, FALSE, FALSE, 0);
895     w.show();
896
897     parameters = ui::Entry();
898     hbox.pack_start(parameters, TRUE, TRUE, 0);
899     parameters.show();
900
901     // -------------------------- //
902
903     w = gtk_hseparator_new();
904     vbox.pack_start(w, FALSE, FALSE, 2);
905     w.show();
906
907     // -------------------------- //
908
909     hbox = ui::HBox(FALSE, 5);
910     vbox.pack_start(hbox, FALSE, FALSE, 0);
911     hbox.show();
912
913     w = ui::Button("Ok");
914     hbox.pack_start(w, TRUE, TRUE, 0);
915     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDOK));
916     w.show();
917
918     gtk_widget_set_can_default(w, true);
919     gtk_widget_grab_default(w);
920
921     w = ui::Button("Cancel");
922     hbox.pack_start(w, TRUE, TRUE, 0);
923     w.connect("clicked", G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(eIDCANCEL));
924     w.show();
925     ret = eIDCANCEL;
926
927     // -------------------------- //
928
929     gtk_window_set_position(window, GTK_WIN_POS_CENTER);
930     window.show();
931     gtk_grab_add(window);
932
933     bool dialogError = TRUE;
934     while (dialogError) {
935         loop = 1;
936         while (loop) {
937             gtk_main_iteration();
938         }
939
940         dialogError = FALSE;
941
942         if (ret == eIDOK) {
943             const char *str = gtk_entry_get_text(GTK_ENTRY(parameters));
944
945             if (!camEventFlags[i][0] || (str && str[0])) {
946                 int type = 0;
947 //                              GList *li;
948
949                 for (type = 1; type < EVENT_COUNT; type++) {
950                     if (gtk_toggle_button_get_active((GtkToggleButton *) eventWidget[type])) {
951                         break;
952                     }
953                 }
954
955                 // Add the event
956                 GetCurrentCam()->GetCam()->addEvent(static_cast<idCameraEvent::eventType>( type ), str,
957                                                     (long) (g_pTimeLine->value));
958
959                 // Refresh event list
960                 RefreshEventList();
961             } else {
962                 dialogError = TRUE;
963             }
964         }
965     }
966
967     gtk_grab_remove(window);
968     window.destroy();
969
970     return TRUE;
971 }
972
973 static gint ci_del(GtkWidget *widget, gpointer data)
974 {
975     // TODO: add support to splines lib
976     if (GetCurrentCam() && GTK_CLIST(g_pEventsList)->focus_row >= 0) {
977         GetCurrentCam()->GetCam()->removeEvent(GTK_CLIST(g_pEventsList)->focus_row);
978         // Refresh event list
979         RefreshEventList();
980     }
981
982     return TRUE;
983 }
984
985 static gint ci_timeline_changed(GtkAdjustment *adjustment)
986 {
987     char buf[128];
988
989     sprintf(buf, "%.2f", adjustment->value / 1000.f);
990     gtk_label_set_text(g_pCurrentTime, buf);
991
992     // FIXME: this will never work completely perfect. Startcamera calls buildcamera, which sets all events to 'nottriggered'.
993     // So if you have a wait at the end of the path, this will go to nontriggered immediately when you go over it and the camera
994     // will have no idea where on the track it should be.
995     if (GetCurrentCam() && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_pTrackCamera))) {
996         float fov;
997         vec3_t origin = {0.0f, 0.0f, 0.0f}, dir = {0.0f, 0.0f, 0.0f}, angles;
998
999         GetCurrentCam()->GetCam()->startCamera(0);
1000
1001         GetCurrentCam()->GetCam()->getCameraInfo((long) (adjustment->value), &origin[0], &dir[0], &fov);
1002         VectorSet(angles, asin(dir[2]) * 180 / 3.14159, atan2(dir[1], dir[0]) * 180 / 3.14159, 0);
1003         g_CameraTable.m_pfnSetCamera(origin, angles);
1004     }
1005
1006     return TRUE;
1007 }
1008
1009 GtkWidget *CreateCameraInspectorDialog(void)
1010 {
1011     GtkWidget *w, *hbox;
1012
1013     // create the window
1014     auto window = ui::Window(ui::window_type::TOP);
1015     gtk_window_set_title(window, "Camera Inspector");
1016     window.connect("delete_event", G_CALLBACK(ci_close), NULL);
1017     window.connect("expose_event", G_CALLBACK(ci_expose), NULL);
1018     //  window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
1019     gtk_window_set_transient_for(window, g_pRadiantWnd);
1020
1021     // don't use show, as you don't want to have it displayed on startup ;-)
1022     gtk_widget_realize(window);
1023
1024     // fill the window
1025
1026     // the table
1027     // -------------------------- //
1028
1029     auto table = ui::Table(3, 2, FALSE);
1030     table.show();
1031     window.add(table);
1032     gtk_container_set_border_width(GTK_CONTAINER(table), 5);
1033     gtk_table_set_row_spacings(table, 5);
1034     gtk_table_set_col_spacings(table, 5);
1035
1036     // the properties column
1037     // -------------------------- //
1038
1039     vbox = ui::VBox(FALSE, 5);
1040     vbox.show();
1041     table.attach(vbox, {0, 1, 0, 1}, {GTK_EXPAND | GTK_FILL, GTK_FILL});
1042
1043     // -------------------------- //
1044
1045     hbox = ui::HBox(FALSE, 5);
1046     vbox.pack_start(hbox, FALSE, FALSE, 0);
1047     hbox.show();
1048
1049     w = ui::Label("File:");
1050     hbox.pack_start(w, FALSE, FALSE, 0);
1051     w.show();
1052
1053     g_pCamListCombo = gtk_combo_new();
1054     hbox.pack_start(g_pCamListCombo, TRUE, TRUE, 0);
1055     g_pCamListCombo.show();
1056
1057     // -------------------------- //
1058
1059     hbox = ui::HBox(FALSE, 5);
1060     vbox.pack_start(hbox, FALSE, FALSE, 0);
1061     hbox.show();
1062
1063     w = ui::Label("Name:");
1064     hbox.pack_start(w, FALSE, FALSE, 0);
1065     w.show();
1066
1067     g_pCamName = ui::Entry();
1068     hbox.pack_start(g_pCamName, FALSE, FALSE, 0);
1069     g_pCamName.show();
1070
1071     w = ui::Label("Type: ");
1072     hbox.pack_start(w, FALSE, FALSE, 0);
1073     w.show();
1074
1075     w = ui::Label("");
1076     hbox.pack_start(w, FALSE, FALSE, 0);
1077     w.show();
1078     g_pCamType = GTK_LABEL(w);
1079
1080     RefreshCamListCombo();
1081
1082     gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(g_pCamListCombo)->entry), FALSE);
1083     (GTK_COMBO(g_pCamListCombo)->entry).connect("changed", G_CALLBACK(ci_camlist_changed), NULL);
1084
1085     // -------------------------- //
1086
1087     auto frame = ui::Frame("Path and Target editing");
1088     frame.show();
1089     table.attach(frame, {0, 1, 1, 2}, {GTK_EXPAND | GTK_FILL, GTK_FILL});
1090
1091     auto vbox = ui::VBox(FALSE, 5);
1092     frame.add(vbox);
1093     gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1094     vbox.show();
1095
1096     // -------------------------- //
1097
1098     hbox = ui::HBox(FALSE, 5);
1099     vbox.pack_start(hbox, FALSE, FALSE, 0);
1100     hbox.show();
1101
1102     w = ui::Label("Edit:");
1103     hbox.pack_start(w, FALSE, FALSE, 0);
1104     w.show();
1105
1106     g_pPathListCombo = gtk_combo_new();
1107     hbox.pack_start(g_pPathListCombo, TRUE, TRUE, 0);
1108     g_pPathListCombo.show();
1109
1110     RefreshPathListCombo();
1111
1112     gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(g_pPathListCombo)->entry), FALSE);
1113     (GTK_COMBO(g_pPathListCombo)->entry).connect("changed", G_CALLBACK(ci_pathlist_changed), NULL);
1114
1115     // -------------------------- //
1116
1117     hbox = ui::HBox(FALSE, 5);
1118     vbox.pack_start(hbox, FALSE, FALSE, 0);
1119     hbox.show();
1120
1121     g_pEditModeEditRadioButton = gtk_radio_button_new_with_label(g_pEditTypeRadio, "Edit Points");
1122     hbox.pack_start(g_pEditModeEditRadioButton, FALSE, FALSE, 3);
1123     g_pEditModeEditRadioButton.show();
1124     g_pEditTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(g_pEditModeEditRadioButton));
1125
1126     g_pEditModeEditRadioButton.connect("clicked", G_CALLBACK(ci_editmode_edit), NULL);
1127
1128     g_pEditModeAddRadioButton = gtk_radio_button_new_with_label(g_pEditTypeRadio, "Add Points");
1129     hbox.pack_start(g_pEditModeAddRadioButton, FALSE, FALSE, 3);
1130     g_pEditModeAddRadioButton.show();
1131     g_pEditTypeRadio = gtk_radio_button_get_group(GTK_RADIO_BUTTON(g_pEditModeAddRadioButton));
1132
1133     g_pEditModeAddRadioButton.connect("clicked", G_CALLBACK(ci_editmode_add), NULL);
1134
1135     // see if we should use a different default
1136     if (g_iEditMode == 1) {
1137         // Go to editmode Add
1138         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_pEditModeAddRadioButton), TRUE);
1139     }
1140
1141     w = ui::Label("Type: ");
1142     hbox.pack_start(w, FALSE, FALSE, 0);
1143     w.show();
1144
1145     w = ui::Label("");
1146     hbox.pack_start(w, FALSE, FALSE, 0);
1147     w.show();
1148     g_pPathType = GTK_LABEL(w);
1149
1150     // -------------------------- //
1151
1152     hbox = ui::HBox(FALSE, 5);
1153     vbox.pack_start(hbox, FALSE, FALSE, 0);
1154     hbox.show();
1155
1156     w = ui::Button("Rename...");
1157     hbox.pack_start(w, FALSE, TRUE, 0);
1158     w.connect("clicked", G_CALLBACK(ci_rename), NULL);
1159     w.show();
1160
1161     w = ui::Button("Add Target...");
1162     hbox.pack_start(w, FALSE, TRUE, 0);
1163     w.connect("clicked", G_CALLBACK(ci_add_target), NULL);
1164     w.show();
1165
1166     // not available in splines library
1167     /*w = gtk_button_new_with_label( "Delete Selected" );
1168        hbox.pack_start( w, FALSE, TRUE, 0);
1169        w.connect( "clicked", G_CALLBACK( ci_delete_selected ), NULL );
1170        w.show();
1171
1172        w = gtk_button_new_with_label( "Select All" );
1173        hbox.pack_start( w, FALSE, TRUE, 0);
1174        w.connect( "clicked", G_CALLBACK( ci_select_all ), NULL );
1175        w.show();*/
1176
1177     // -------------------------- //
1178
1179     frame = ui::Frame("Time");
1180     frame.show();
1181     table.attach(frame, {0, 1, 2, 3}, {GTK_EXPAND | GTK_FILL, GTK_FILL});
1182
1183     vbox = ui::VBox(FALSE, 5);
1184     frame.add(vbox);
1185     gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1186     vbox.show();
1187
1188     // -------------------------- //
1189
1190     hbox = ui::HBox(FALSE, 5);
1191     vbox.pack_start(hbox, FALSE, FALSE, 0);
1192     hbox.show();
1193
1194     w = ui::Label("Length (seconds):");
1195     hbox.pack_start(w, FALSE, FALSE, 0);
1196     w.show();
1197
1198     g_pSecondsEntry = ui::Entry();
1199     hbox.pack_start(g_pSecondsEntry, FALSE, FALSE, 0);
1200     g_pSecondsEntry.show();
1201
1202     // -------------------------- //
1203
1204     hbox = ui::HBox(FALSE, 5);
1205     vbox.pack_start(hbox, FALSE, FALSE, 0);
1206     hbox.show();
1207
1208     w = ui::Label("Current Time: ");
1209     hbox.pack_start(w, FALSE, FALSE, 0);
1210     w.show();
1211
1212     w = ui::Label("0.00");
1213     hbox.pack_start(w, FALSE, FALSE, 0);
1214     w.show();
1215     g_pCurrentTime = GTK_LABEL(w);
1216
1217     w = ui::Label(" of ");
1218     hbox.pack_start(w, FALSE, FALSE, 0);
1219     w.show();
1220
1221     w = ui::Label("0.00");
1222     hbox.pack_start(w, FALSE, FALSE, 0);
1223     w.show();
1224     g_pTotalTime = GTK_LABEL(w);
1225
1226     // -------------------------- //
1227
1228     hbox = ui::HBox(FALSE, 5);
1229     vbox.pack_start(hbox, FALSE, FALSE, 0);
1230     hbox.show();
1231
1232     g_pTimeLine = ui::Adjustment(0, 0, 30000, 100, 250, 0);
1233     g_pTimeLine.connect("value_changed", G_CALLBACK(ci_timeline_changed), NULL);
1234     w = ui::HScale(g_pTimeLine);
1235     hbox.pack_start(w, TRUE, TRUE, 0);
1236     w.show();
1237     gtk_scale_set_draw_value(GTK_SCALE(w), FALSE);
1238
1239     // -------------------------- //
1240
1241     hbox = ui::HBox(FALSE, 5);
1242     vbox.pack_start(hbox, FALSE, FALSE, 0);
1243     hbox.show();
1244
1245     g_pTrackCamera = ui::CheckButton("Track Camera");
1246     hbox.pack_start(g_pTrackCamera, FALSE, FALSE, 0);
1247     g_pTrackCamera.show();
1248
1249     // -------------------------- //
1250
1251     hbox = ui::HBox(FALSE, 5);
1252     vbox.pack_start(hbox, FALSE, FALSE, 0);
1253     hbox.show();
1254
1255     w = ui::Label("Events:");
1256     hbox.pack_start(w, FALSE, FALSE, 0);
1257     w.show();
1258
1259     // -------------------------- //
1260
1261     hbox = ui::HBox(FALSE, 5);
1262     vbox.pack_start(hbox, FALSE, FALSE, 0);
1263     hbox.show();
1264
1265     auto scr = w = ui::ScrolledWindow();
1266     w.dimensions(0, 150);
1267     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1268     hbox.pack_start(w, TRUE, TRUE, 0);
1269     w.show();
1270
1271     g_pEventsList = gtk_clist_new(3);
1272     scr.add(g_pEventsList);
1273     //g_pEventsList.connect( "select_row", G_CALLBACK (proplist_select_row), NULL);
1274     gtk_clist_set_selection_mode(GTK_CLIST(g_pEventsList), GTK_SELECTION_BROWSE);
1275     gtk_clist_column_titles_hide(GTK_CLIST(g_pEventsList));
1276     gtk_clist_set_column_auto_resize(GTK_CLIST(g_pEventsList), 0, TRUE);
1277     gtk_clist_set_column_auto_resize(GTK_CLIST(g_pEventsList), 1, TRUE);
1278     gtk_clist_set_column_auto_resize(GTK_CLIST(g_pEventsList), 2, TRUE);
1279     g_pEventsList.show();
1280
1281     vbox = ui::VBox(FALSE, 5);
1282     hbox.pack_start(vbox, FALSE, FALSE, 0);
1283     vbox.show();
1284
1285     w = ui::Button("Add...");
1286     vbox.pack_start(w, FALSE, FALSE, 0);
1287     w.connect("clicked", G_CALLBACK(ci_add), NULL);
1288     w.show();
1289
1290     w = ui::Button("Del");
1291     vbox.pack_start(w, FALSE, FALSE, 0);
1292     w.connect("clicked", G_CALLBACK(ci_del), NULL);
1293     w.show();
1294
1295     // -------------------------- //
1296
1297     /*/
1298     |
1299     |
1300     |
1301     * /
1302
1303     // the buttons column
1304     // -------------------------- //
1305
1306     vbox = gtk_vbox_new( FALSE, 5 );
1307     vbox.show();
1308     table.attach(vbox, {1, 2, 0, 1}, {GTK_FILL, GTK_FILL});
1309
1310     w = gtk_button_new_with_label( "New..." );
1311     vbox.pack_start( w, FALSE, FALSE, 0 );
1312     w.connect( "clicked", G_CALLBACK( ci_new ), NULL );
1313     w.show();
1314
1315     w = gtk_button_new_with_label( "Load..." );
1316     vbox.pack_start( w, FALSE, FALSE, 0 );
1317     w.connect( "clicked", G_CALLBACK( ci_load ), NULL );
1318     w.show();
1319
1320     // -------------------------- //
1321
1322     vbox = gtk_vbox_new( FALSE, 5 );
1323     vbox.show();
1324     table.attach( vbox, {1, 2, 1, 2}, {GTK_FILL, GTK_FILL});
1325
1326     w = gtk_button_new_with_label( "Save..." );
1327     vbox.pack_start( w, FALSE, FALSE, 0 );
1328     w.connect( "clicked", G_CALLBACK( ci_save ), NULL );
1329     w.show();
1330
1331     w = gtk_button_new_with_label( "Unload" );
1332     vbox.pack_start( w, FALSE, FALSE, 0 );
1333     w.connect( "clicked", G_CALLBACK( ci_unload ), NULL );
1334     w.show();
1335
1336     hbox = gtk_hbox_new( FALSE, 5 );
1337     vbox.pack_start( hbox, TRUE, TRUE, 0 );
1338     hbox.show();
1339
1340     w = gtk_button_new_with_label( "Apply" );
1341     vbox.pack_start( w, FALSE, FALSE, 0 );
1342     w.connect( "clicked", G_CALLBACK( ci_apply ), NULL );
1343     w.show();
1344
1345     w = gtk_button_new_with_label( "Preview" );
1346     vbox.pack_start( w, FALSE, FALSE, 0 );
1347     w.connect( "clicked", G_CALLBACK( ci_preview ), NULL );
1348     w.show();
1349
1350     // -------------------------- //
1351
1352     vbox = gtk_vbox_new( FALSE, 5 );
1353     vbox.show();
1354     table.attach(vbox, {1, 2, 2, 3}, {GTK_FILL, GTK_FILL});
1355
1356     hbox = gtk_hbox_new( FALSE, 5 );
1357     vbox.pack_start( hbox, TRUE, TRUE, 0 );
1358     hbox.show();
1359
1360     w = gtk_button_new_with_label( "Close" );
1361     vbox.pack_start( w, FALSE, FALSE, 0 );
1362     w.connect( "clicked", G_CALLBACK( ci_close ), NULL );
1363     gtk_widget_set_can_default( w, true );
1364     gtk_widget_grab_default( w );
1365     w.show();
1366
1367     // -------------------------- //
1368
1369     return window;
1370 }