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