1 #include "SkinChooser.h"
2
3 #include "eclasslib.h"
4 #include "iradiant.h"
5 #include "radiant_i18n.h"
6
7 #include "gtkutil/image.h"
8 #include "gtkutil/RightAlignment.h"
9 #include "gtkutil/ScrolledFrame.h"
10 #include "gtkutil/IconTextColumn.h"
11 #include "gtkutil/TreeModel.h"
12 #include "gtkutil/VFSTreePopulator.h"
13
14 #include <gtk/gtk.h>
15
16 namespace ui {
17
18 /* CONSTANTS */
19
20 namespace {
21
22 const char* FOLDER_ICON = "folder16.png";
23 const char* SKIN_ICON = "skin16.png";
24
25 // Tree column enum
26 enum
27 {
28 DISPLAYNAME_COL, FULLNAME_COL, ICON_COL, N_COLUMNS
29 };
30
31 }
32
33 // Constructor
SkinChooser()34 SkinChooser::SkinChooser () :
35 _widget(gtk_window_new(GTK_WINDOW_TOPLEVEL)), _lastSkin("")
36 {
37 // Set up window
38 gtk_window_set_transient_for(GTK_WINDOW(_widget), GlobalRadiant().getMainWindow());
39 gtk_window_set_modal(GTK_WINDOW(_widget), TRUE);
40 gtk_window_set_position(GTK_WINDOW(_widget), GTK_WIN_POS_CENTER_ON_PARENT);
41 gtk_window_set_title(GTK_WINDOW(_widget), "Choose skin");
42 g_signal_connect(G_OBJECT(_widget),
43 "delete-event",
44 G_CALLBACK(_onCloseButton),
45 this);
46
47 // Set the default size of the window
48 GdkScreen* scr = gtk_window_get_screen(GTK_WINDOW(_widget));
49 gint w = gdk_screen_get_width(scr);
50
51 // Main vbox
52 GtkWidget* vbx = gtk_vbox_new(FALSE, 6);
53
54 // HBox containing tree view and preview
55 GtkWidget* hbx = gtk_hbox_new(FALSE, 12);
56 gtk_box_pack_start(GTK_BOX(hbx), createTreeView(w / 5), TRUE, TRUE, 0);
57 gtk_box_pack_start(GTK_BOX(hbx), createPreview(w / 3), FALSE, FALSE, 0);
58
59 gtk_box_pack_start(GTK_BOX(vbx), hbx, TRUE, TRUE, 0);
60 gtk_box_pack_end(GTK_BOX(vbx), createButtons(), FALSE, FALSE, 0);
61 gtk_container_set_border_width(GTK_CONTAINER(_widget), 6);
62 gtk_container_add(GTK_CONTAINER(_widget), vbx);
63 }
64
65 // Create the TreeView
createTreeView(gint width)66 GtkWidget* SkinChooser::createTreeView (gint width)
67 {
68 // Create the treestore
69 _treeStore = gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF);
70
71 // Create the tree view
72 _treeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(_treeStore));
73 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(_treeView), FALSE);
74
75 // Single column to display the skin name
76 gtk_tree_view_append_column(GTK_TREE_VIEW(_treeView), gtkutil::IconTextColumn("Skin", DISPLAYNAME_COL, ICON_COL));
77
78 // Connect up selection changed callback
79 _selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(_treeView));
80 g_signal_connect(G_OBJECT(_selection),
81 "changed",
82 G_CALLBACK(_onSelChanged),
83 this);
84
85 // Pack treeview into a ScrolledFrame and return
86 gtk_widget_set_size_request(_treeView, width, -1);
87 return gtkutil::ScrolledFrame(_treeView);
88 }
89
90 // Create the model preview
createPreview(gint size)91 GtkWidget* SkinChooser::createPreview (gint size)
92 {
93 _preview.setSize(size);
94 return _preview;
95 }
96
97 // Create the buttons panel
createButtons()98 GtkWidget* SkinChooser::createButtons ()
99 {
100 GtkWidget* hbx = gtk_hbox_new(TRUE, 6);
101
102 GtkWidget* okButton = gtk_button_new_from_stock(GTK_STOCK_OK);
103 GtkWidget* cancelButton = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
104
105 g_signal_connect(G_OBJECT(okButton), "clicked",
106 G_CALLBACK(_onOK), this);
107 g_signal_connect(G_OBJECT(cancelButton), "clicked",
108 G_CALLBACK(_onCancel), this);
109
110 gtk_box_pack_end(GTK_BOX(hbx), okButton, TRUE, TRUE, 0);
111 gtk_box_pack_end(GTK_BOX(hbx), cancelButton, TRUE, TRUE, 0);
112
113 return gtkutil::RightAlignment(hbx);
114 }
115
116 // Show the dialog and block for a selection
showAndBlock(const std::string & model,const std::string & prev)117 std::string SkinChooser::showAndBlock (const std::string& model, const std::string& prev)
118 {
119 // Set the model and previous skin, then populate the skins
120 _model = model;
121 _prevSkin = prev;
122 populateSkins();
123
124 // Show the dialog
125 gtk_widget_show_all(_widget);
126 _preview.initialisePreview();
127
128 // Enter main loop and block
129 gtk_main();
130
131 // Hide the dialog and return the selection
132 _preview.setModel(""); // release model
133 gtk_widget_hide(_widget);
134 return _lastSkin;
135 }
136
137 namespace {
138
139 /*
140 * Visitor class to fill in column data for the skins tree.
141 */
142 class SkinTreeVisitor: public gtkutil::VFSTreePopulator::Visitor
143 {
144 public:
145
146 // Required visit function
visit(GtkTreeStore * store,GtkTreeIter * it,const std::string & path,bool isExplicit)147 void visit (GtkTreeStore* store, GtkTreeIter* it, const std::string& path, bool isExplicit)
148 {
149 // Get the display path, everything after rightmost slash
150 std::string displayPath = path.substr(path.rfind("/") + 1);
151
152 // Get the icon, either folder or skin
153 GdkPixbuf* pixBuf = isExplicit ? gtkutil::getLocalPixbuf(SKIN_ICON)
154 : gtkutil::getLocalPixbuf(FOLDER_ICON);
155
156 gtk_tree_store_set(store, it, DISPLAYNAME_COL, displayPath.c_str(), FULLNAME_COL, path.c_str(), ICON_COL,
157 pixBuf, -1);
158 }
159 };
160
161 } // namespace
162
163 // Populate the list of skins
populateSkins()164 void SkinChooser::populateSkins ()
165 {
166 // Clear the treestore
167 gtk_tree_store_clear(_treeStore);
168
169 // Add the "Matching skins" toplevel node
170 GtkTreeIter matchingSkins;
171 gtk_tree_store_append(_treeStore, &matchingSkins, NULL);
172 gtk_tree_store_set(_treeStore, &matchingSkins, DISPLAYNAME_COL, _("Matching skins"), FULLNAME_COL, "", ICON_COL,
173 gtkutil::getLocalPixbuf(FOLDER_ICON), -1);
174
175 // Get the skins for the associated model, and add them as matching skins
176 // TODO: mattn
177 const StringList matchList; // = GlobalModelSkinCache().getSkinsForModel(_model);
178 for (StringList::const_iterator i = matchList.begin(); i != matchList.end(); ++i) {
179 GtkTreeIter temp;
180 gtk_tree_store_append(_treeStore, &temp, &matchingSkins);
181 gtk_tree_store_set(_treeStore, &temp, DISPLAYNAME_COL, i->c_str(), FULLNAME_COL, i->c_str(), ICON_COL,
182 gtkutil::getLocalPixbuf(SKIN_ICON), -1);
183 }
184 }
185
186 // Static method to display singleton instance and choose a skin
chooseSkin(const std::string & model,const std::string & prev)187 std::string SkinChooser::chooseSkin (const std::string& model, const std::string& prev)
188 {
189 // The static instance
190 static SkinChooser _instance;
191
192 // Show and block the instance, returning the selected skin
193 return _instance.showAndBlock(model, prev);
194 }
195
196 /* GTK CALLBACKS */
197
_onOK(GtkWidget * widget,SkinChooser * self)198 void SkinChooser::_onOK (GtkWidget* widget, SkinChooser* self)
199 {
200 // Get the selected skin
201 self->_lastSkin = gtkutil::TreeModel::getSelectedString(self->_selection, FULLNAME_COL);
202 // Exit main loop
203 gtk_main_quit();
204 }
205
_onCancel(GtkWidget * widget,SkinChooser * self)206 void SkinChooser::_onCancel (GtkWidget* widget, SkinChooser* self)
207 {
208 // Clear the last skin and quit the main loop
209 self->_lastSkin = self->_prevSkin;
210 gtk_main_quit();
211 }
212
_onSelChanged(GtkWidget *,SkinChooser * self)213 void SkinChooser::_onSelChanged (GtkWidget*, SkinChooser* self)
214 {
215 // Get the selected skin
216 std::string skin = gtkutil::TreeModel::getSelectedString(self->_selection, FULLNAME_COL);
217
218 // Set the model preview to show the model with the selected skin
219 self->_preview.setModel(self->_model);
220 self->_preview.setSkin(skin);
221 }
222
_onCloseButton(GtkWidget *,SkinChooser * self)223 bool SkinChooser::_onCloseButton (GtkWidget*, SkinChooser* self)
224 {
225 // Clear the last skin and quit the main loop
226 self->_lastSkin = self->_prevSkin;
227 gtk_main_quit();
228 return true;
229 }
230
231 } // namespace ui
232