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