1 /*
2  *  Copyright 2005-2021 Fabrice Colin
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <iostream>
20 #include <glibmm/convert.h>
21 #include <gtkmm/stock.h>
22 
23 #include "config.h"
24 #include "NLS.h"
25 #include "Url.h"
26 #include "PinotSettings.h"
27 #include "PinotUtils.h"
28 
29 using namespace std;
30 using namespace Glib;
31 using namespace Gtk;
32 
33 /// Open a FileChooserDialog.
select_file_name(const ustring & title,ustring & location,bool openOrCreate,bool directoriesOnly)34 bool select_file_name(const ustring &title, ustring &location,
35 	bool openOrCreate, bool directoriesOnly)
36 {
37 	FileChooserDialog fileChooser(title);
38 
39 	if (title.empty() == true)
40 	{
41 		return false;
42 	}
43 
44 	prepare_file_chooser(fileChooser, location, openOrCreate, directoriesOnly);
45 	fileChooser.show();
46 
47 	int result = fileChooser.run();
48 	if (result == RESPONSE_OK)
49 	{
50 		// Retrieve the chosen location
51 		if (directoriesOnly == false)
52 		{
53 			location = filename_to_utf8(fileChooser.get_filename());
54 		}
55 		else
56 		{
57 			location = filename_to_utf8(fileChooser.get_current_folder());
58 		}
59 
60 		return true;
61 	}
62 
63 	return false;
64 }
65 
prepare_file_chooser(FileChooserDialog & fileChooser,ustring & location,bool openOrCreate,bool directoriesOnly)66 bool prepare_file_chooser(FileChooserDialog &fileChooser, ustring &location,
67 	bool openOrCreate, bool directoriesOnly)
68 {
69 	FileChooserAction chooserAction = FILE_CHOOSER_ACTION_OPEN;
70 	StockID okButtonStockId = Stock::OPEN;
71 	bool isDirectory = false;
72 
73 	if (openOrCreate == false)
74 	{
75 		okButtonStockId = Stock::SAVE;
76 		fileChooser.set_do_overwrite_confirmation(true);
77 	}
78 
79 	// Have we been provided with an initial location ?
80 	if (location.empty() == true)
81 	{
82 		// No, get the location of the home directory then
83 		location = PinotSettings::getInstance().getHomeDirectory();
84 		isDirectory = true;
85 	}
86 
87 	if (directoriesOnly == false)
88 	{
89 		if (openOrCreate == true)
90 		{
91 			chooserAction = FILE_CHOOSER_ACTION_OPEN;
92 		}
93 		else
94 		{
95 			chooserAction = FILE_CHOOSER_ACTION_SAVE;
96 		}
97 	}
98 	else
99 	{
100 		if (openOrCreate == true)
101 		{
102 			chooserAction = FILE_CHOOSER_ACTION_SELECT_FOLDER;
103 		}
104 		else
105 		{
106 			chooserAction = FILE_CHOOSER_ACTION_CREATE_FOLDER;
107 		}
108 		isDirectory = true;
109 	}
110 
111 	fileChooser.set_action(chooserAction);
112 	Url urlObj(location);
113 	fileChooser.set_current_folder(filename_from_utf8(urlObj.getLocation()));
114 	if (isDirectory == false)
115 	{
116 		fileChooser.set_current_name(filename_from_utf8(urlObj.getFile()));
117 	}
118 	fileChooser.set_local_only();
119 	fileChooser.set_select_multiple(false);
120 	fileChooser.add_button(Stock::CANCEL, RESPONSE_CANCEL);
121 	fileChooser.add_button(okButtonStockId, RESPONSE_OK);
122 	fileChooser.set_show_hidden(true);
123 
124 	return true;
125 }
126 
127 /// Get a column height.
get_column_height(TreeView * pTree)128 int get_column_height(TreeView *pTree)
129 {
130 	int height = 0;
131 
132 	if (pTree == NULL)
133 	{
134 		return 0;
135 	}
136 
137 	TreeViewColumn *pColumn = pTree->get_column(1);
138 	if (pColumn != NULL)
139 	{
140 		Gdk::Rectangle cellArea;
141 		int xOffset, yOffset, cellWidth, cellHeight;
142 
143 		pColumn->cell_get_size(cellArea, xOffset, yOffset, cellWidth, cellHeight);
144 		height += cellHeight;
145 #ifdef DEBUG
146 		clog << "get_column_height: cell " << cellHeight << " " << yOffset << endl;
147 #endif
148 	}
149 #ifdef DEBUG
150 	clog << "get_column_height: " << height << endl;
151 #endif
152 
153 	return height;
154 }
155 
156 /// Create a text column.
create_column(const ustring & title,const TreeModelColumnBase & modelColumn,bool isResizable,bool isSortable,const TreeModelColumnBase & sortColumn)157 TreeViewColumn *create_column(const ustring &title, const TreeModelColumnBase& modelColumn,
158 	bool isResizable, bool isSortable, const TreeModelColumnBase &sortColumn)
159 {
160 	TreeViewColumn *pColumn = new TreeViewColumn(title);
161 
162 	CellRendererText *pTextRenderer = new CellRendererText();
163 	pColumn->pack_start(*manage(pTextRenderer));
164 	pColumn->add_attribute(pTextRenderer->property_text(), modelColumn);
165 	pColumn->set_resizable(isResizable);
166 	if (isSortable == true)
167 	{
168 		pColumn->set_sort_column(sortColumn);
169 	}
170 
171 	return pColumn;
172 }
173 
174 /// Create an icon and text column, rendered by renderTextAndIconCell.
create_column_with_icon(const ustring & title,const TreeModelColumnBase & modelColumn,const TreeViewColumn::SlotCellData & renderTextAndIconCell,bool isResizable,bool isSortable,const TreeModelColumnBase & sortColumn)175 TreeViewColumn *create_column_with_icon(const ustring &title, const TreeModelColumnBase& modelColumn,
176 	const TreeViewColumn::SlotCellData &renderTextAndIconCell,
177 	bool isResizable, bool isSortable, const TreeModelColumnBase &sortColumn)
178 {
179 	TreeViewColumn *pColumn = new TreeViewColumn(title);
180 
181 	// Pack an icon renderer in the column
182 	CellRendererPixbuf *pIconRenderer = new CellRendererPixbuf();
183 	pColumn->pack_start(*manage(pIconRenderer), false);
184 	pColumn->set_cell_data_func(*pIconRenderer, renderTextAndIconCell);
185 	// ...followed by a text renderer
186 	CellRendererText *pTextRenderer = new CellRendererText();
187 	pColumn->pack_start(*manage(pTextRenderer));
188 	pColumn->set_cell_data_func(*pTextRenderer, renderTextAndIconCell);
189 
190 	pColumn->add_attribute(pTextRenderer->property_text(), modelColumn);
191 	pColumn->set_resizable(isResizable);
192 	if (isSortable == true)
193 	{
194 		pColumn->set_sort_column(sortColumn);
195 	}
196 
197 	return pColumn;
198 }
199 
200 /// Converts to UTF-8.
to_utf8(const string & text)201 ustring to_utf8(const string &text)
202 {
203 	std::string charset;
204 
205 	// Get the locale charset
206 	get_charset(charset);
207 	// Call overload
208 	return to_utf8(text, charset);
209 }
210 
211 /// Converts from the given charset to UTF-8.
to_utf8(const string & text,const string & charset)212 ustring to_utf8(const string &text, const string &charset)
213 {
214 	if ((charset == "UTF-8") ||
215 		(charset == "utf-8"))
216 	{
217 		// No conversion necessary
218 		return text;
219 	}
220 
221 	try
222 	{
223 		if (charset.empty() == false)
224 		{
225 			return convert_with_fallback(text, "UTF-8", charset, " ");
226 		}
227 		else
228 		{
229 			return locale_to_utf8(text);
230 		}
231 	}
232 	catch (Error &ce)
233 	{
234 #ifdef DEBUG
235 		clog << "to_utf8: cannot convert from " << charset << ": " << ce.what() << endl;
236 #endif
237 		if (charset.empty() == false)
238 		{
239 			return to_utf8(text);
240 		}
241 	}
242 	catch (...)
243 	{
244 #ifdef DEBUG
245 		clog << "to_utf8: unknown exception" << endl;
246 #endif
247 	}
248 
249 	return "";
250 }
251 
252 /// Converts from UTF-8.
from_utf8(const ustring & text)253 string from_utf8(const ustring &text)
254 {
255 	try
256 	{
257 		return locale_from_utf8(text);
258 	}
259 	catch (Error &ce)
260 	{
261 #ifdef DEBUG
262 		clog << "from_utf8: " << ce.what() << endl;
263 #endif
264 	}
265 	catch (...)
266 	{
267 #ifdef DEBUG
268 		clog << "from_utf8: unknown exception" << endl;
269 #endif
270 	}
271 
272 	return "";
273 }
274