1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  file selectlist.c
4  *  Copyright (C) 1998--2003  Guido Masarotto and Brian Ripley
5  *  Copyright (C) 2004	      The R Foundation
6  *  Copyright (C) 2005--2020  The R Core Team
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, a copy is available at
20  *  https://www.R-project.org/Licenses/
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <Defn.h>
28 #include "graphapp/ga.h"
29 #include <rui.h> // RConsole
30 #include <windows.h>
31 
32 #include "win-nls.h"
33 
34 static window wselect;
35 static button bFinish, bCancel;
36 static listbox f_list;
37 static int done;
38 
cleanup(void)39 static void cleanup(void)
40 {
41     hide(wselect);
42     delobj(f_list); delobj(bFinish); delobj(bCancel);
43     delobj(wselect);
44 }
45 
46 
cancel(button b)47 static void cancel(button b)
48 {
49     done = 2;
50 }
51 
finish(button b)52 static void finish(button b)
53 {
54     done = 1;
55 }
56 
key1(control c,int ch)57 static void key1(control c, int ch)
58 {
59     if(ch == '\n') finish(NULL);
60     if(ch == ESC)  cancel(NULL);
61 }
62 
63 rect getSysFontSize(void); /* in graphapp/fonts.c */
64 RECT *RgetMDIsize(void); /* in rui.c */
65 
Win_selectlist(SEXP args)66 SEXP Win_selectlist(SEXP args)
67 {
68     SEXP choices, preselect, ans = R_NilValue;
69     const char **clist;
70     int i, j = -1, n, mw = 0, multiple, nsel = 0;
71     int xmax, ymax, ylist, fht, h0;
72     Rboolean haveTitle;
73 
74     choices = CAR(args);
75     if(!isString(choices)) error(_("invalid '%s' argument"), "choices");
76     preselect = CADR(args);
77     if(!isNull(preselect) && !isString(preselect))
78 	error(_("invalid '%s' argument"), "preselect");
79     multiple = asLogical(CADDR(args));
80     if(multiple == NA_LOGICAL) multiple = 0;
81     haveTitle = isString(CADDDR(args));
82     if(!multiple && isString(preselect) && LENGTH(preselect) != 1)
83 	error(_("invalid '%s' argument"), "preselect");
84 
85     n = LENGTH(choices);
86     clist = (const char **) R_alloc(n + 1, sizeof(char *));
87     for(i = 0; i < n; i++) {
88 	clist[i] = translateChar(STRING_ELT(choices, i));
89 	mw = max(mw, gstrwidth(NULL, SystemFont, clist[i]));
90     }
91     clist[n] = NULL;
92 
93     fht = getSysFontSize().height;
94 
95     xmax = max(170, mw+60); /* allow for scrollbar */
96     if(ismdi()) {
97 	RECT *pR = RgetMDIsize();
98 	h0 = pR->bottom;
99     } else {
100 	h0 = deviceheight(NULL);
101     }
102     ymax = min(80+fht*n, h0-100); /* allow for window widgets, toolbar */
103     ylist = ymax - 60;
104     wselect = newwindow(haveTitle ? translateChar(STRING_ELT(CADDDR(args), 0)):
105 			(multiple ? _("Select one or more") : _("Select one")),
106 			rect(0, 0, xmax, ymax),
107 			Titlebar | Centered | Modal | Floating);
108     setbackground(wselect, dialog_bg());
109     if(multiple)
110 	f_list = newmultilist(clist, rect(10, 10, xmax-25, ylist), NULL, finish);
111     else
112 	f_list = newlistbox(clist, rect(10, 10, xmax-25, ylist), NULL, finish);
113     if(!isNull(preselect) && LENGTH(preselect)) {
114 	for(i = 0; i < n; i++)
115 	    for(j = 0; j < LENGTH(preselect); j++)
116 		if(strcmp(clist[i], translateChar(STRING_ELT(preselect, j))) == 0) {
117 		    setlistitem(f_list, i);
118 		    break;
119 		}
120     }
121     bFinish = newbutton(G_("OK"), rect(xmax-160, ymax-40, 70, 25), finish);
122     bCancel = newbutton(G_("Cancel"), rect(xmax-80, ymax-40, 70, 25), cancel);
123     setkeydown(wselect, key1);
124     show(wselect);
125     done = 0;
126     while(!done) {
127 	R_WaitEvent();
128 	R_ProcessEvents();
129     }
130 
131     if(multiple) {
132 	if (done == 1) { /* Finish */
133 	    for(i = 0; i < n; i++)  if(isselected(f_list, i)) nsel++;
134 	    PROTECT(ans = allocVector(STRSXP, nsel));
135 	    for(i = 0, j = 0; i < n; i++)
136 		if(isselected(f_list, i))
137 		    SET_STRING_ELT(ans, j++, mkChar(clist[i]));
138 	} else { /* cancel */
139 	    PROTECT(ans = allocVector(STRSXP, 0));
140 	}
141     } else {
142 	if (done == 1) { /* Finish */
143 	    i = getlistitem(f_list);
144 	    if (i >= 0)
145 		PROTECT(ans = mkString(clist[i]));
146 	    else
147 		PROTECT(ans = mkString("")); /* error/unreachable */
148 	} else { /* cancel */
149 	    PROTECT(ans = mkString(""));
150 	}
151     }
152 
153     cleanup();
154     show(RConsole);
155     R_ProcessEvents();
156     UNPROTECT(1);
157     return ans;
158 }
159 
countFilenamesW(const wchar_t * list)160 static int countFilenamesW(const wchar_t *list)
161 {
162     const wchar_t *temp;
163     int count;
164     count = 0;
165     for (temp = list; *temp; temp += wcslen(temp)+1) count++;
166     return count;
167 }
168 
169 
mkCharUTF8(const wchar_t * wc)170 static SEXP mkCharUTF8(const wchar_t *wc)
171 {
172     char s[4*MAX_PATH + 1];
173     wcstoutf8(s, wc, sizeof(s));
174     return mkCharCE(s, CE_UTF8);
175 }
176 
177 
chooseFiles(SEXP def,SEXP caption,SEXP smulti,SEXP filters,SEXP sindex)178 SEXP chooseFiles(SEXP def, SEXP caption, SEXP smulti, SEXP filters, SEXP sindex)
179 {
180     wchar_t *temp, *res, *cfilters;
181     const wchar_t *p;
182     wchar_t path[32768], filename[32768];
183     int multi, filterindex, i, count, lfilters, pathlen;
184 
185     multi = asLogical(smulti);
186     filterindex = asInteger(sindex);
187     if(length(def) != 1 )
188 	error(_("'default' must be a character string"));
189     p = filenameToWchar(STRING_ELT(def, 0), 1);
190     if(wcslen(p) >= 32768) error(_("'default' is overlong"));
191     wcscpy(path, p);
192     for(temp = path; *temp; temp++) if(*temp == L'/') *temp = L'\\';
193     if(length(caption) != 1 )
194 	error(_("'caption' must be a character string"));
195     if(multi == NA_LOGICAL)
196 	error(_("'multi' must be a logical value"));
197     if(filterindex == NA_INTEGER)
198 	error(_("'filterindex' must be an integer value"));
199     lfilters = 1 + length(filters);
200     for (i = 0; i < length(filters); i++)
201 	lfilters += wcslen(filenameToWchar(STRING_ELT(filters, i), 0));
202     cfilters = (wchar_t *) R_alloc(lfilters, sizeof(wchar_t));
203     temp = cfilters;
204     for (i = 0; i < length(filters)/2; i++) {
205 	wcscpy(temp, filenameToWchar(STRING_ELT(filters, i), 0));
206 	temp += wcslen(temp)+1;
207 	wcscpy(temp, filenameToWchar(STRING_ELT(filters, i+length(filters)/2),
208 				     0));
209 	temp += wcslen(temp)+1;
210     }
211     *temp = 0;
212 
213     res = askfilenamesW(filenameToWchar(STRING_ELT(caption, 0), 0), path,
214 			multi, cfilters, filterindex, NULL);
215 
216     if (multi)
217     	count = countFilenamesW(res);
218     else
219     	count = wcslen(res) ? 1 : 0;
220 
221     SEXP ans;
222     if (count < 2) PROTECT(ans = allocVector(STRSXP, count));
223     else PROTECT(ans = allocVector(STRSXP, count-1));
224 
225     switch (count) {
226     case 0: break;
227     case 1: SET_STRING_ELT(ans, 0, mkCharUTF8(res));
228 	break;
229     default:
230 	wcsncpy(path, res, 32768);
231 	pathlen = wcslen(path);
232 	if (path[pathlen-1] == L'\\') path[--pathlen] = L'\0';
233 	temp = res;
234 	for (i = 0; i < count-1; i++) {
235 	    temp += wcslen(temp) + 1;
236 	    if (wcschr(temp,L':') || *temp == L'\\' || *temp == L'/')
237 		SET_STRING_ELT(ans, i, mkCharUTF8(temp));
238 	    else {
239 		wcsncpy(filename, path, 32768);
240 		filename[pathlen] = L'\\';
241 		wcsncpy(filename+pathlen+1, temp, 32768-pathlen-1);
242 		SET_STRING_ELT(ans, i, mkCharUTF8(filename));
243 	    }
244 	}
245     }
246     UNPROTECT(1);
247     return ans;
248 }
249 
chooseDir(SEXP def,SEXP caption)250 SEXP chooseDir(SEXP def, SEXP caption)
251 {
252     const char *p;
253     char path[MAX_PATH];
254 
255     if(!isString(def) || length(def) != 1 )
256 	error(_("'default' must be a character string"));
257     p = translateChar(STRING_ELT(def, 0));
258     if(strlen(p) >= MAX_PATH) error(_("'default' is overlong"));
259     strcpy(path, R_ExpandFileName(p));
260     R_fixbackslash(path);
261     if(!isString(caption) || length(caption) != 1 )
262 	error(_("'caption' must be a character string"));
263     p = askcdstring(translateChar(STRING_ELT(caption, 0)), path);
264 
265     SEXP ans = PROTECT(allocVector(STRSXP, 1));
266     SET_STRING_ELT(ans, 0, p ? mkChar(p): NA_STRING);
267     UNPROTECT(1);
268     return ans;
269 }
270