Compiling Fl_Native_File_Chooser_WIN32.cxx...
fl_dnd_win32.cxx:436:11: warning: private field 'm_EnumF' is not used [-Wunused-private-field]
FLEnum *m_EnumF;
^
1 warning generated.
Fl_Native_File_Chooser_WIN32.cxx:378:19: warning: use of bitwise '|' with boolean operands [-Wbitwise-instead-of-logical]
bool unixpath = IsUnixPath(_directory) | IsUnixPath(_preset_file); // caller uses unix paths?
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
||
Fl_Native_File_Chooser_WIN32.cxx:378:19: note: cast one or both operands to int to silence this warning
1 warning generated.
1062 lines
34 KiB
C++
1062 lines
34 KiB
C++
//
|
|
// FLTK native OS file chooser widget
|
|
//
|
|
// Copyright 1998-2021 by Bill Spitzak and others.
|
|
// Copyright 2004 Greg Ercolano.
|
|
// API changes + filter improvements by Nathan Vander Wilt 2005
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// https://www.fltk.org/COPYING.php
|
|
//
|
|
// Please see the following page on how to report bugs and issues:
|
|
//
|
|
// https://www.fltk.org/bugs.php
|
|
//
|
|
|
|
// Any application to multi-folder implementation:
|
|
// http://www.codeproject.com/dialog/selectfolder.asp
|
|
//
|
|
|
|
#ifndef FL_DOXYGEN // PREVENT DOXYGEN'S USE OF THIS FILE
|
|
#include <FL/Enumerations.H>
|
|
|
|
#include <stdlib.h> // malloc
|
|
#include <stdio.h> // snprintf
|
|
#include <wchar.h>
|
|
|
|
#define FNFC_MAX_PATH 32768 // XXX: MAX_PATH under win32 is 260, too small for modern use
|
|
|
|
#include <FL/fl_string_functions.h> // fl_strdup()
|
|
#include "flstring.h" // fl_strlcpy()/cat()
|
|
#include <FL/Fl_Native_File_Chooser.H>
|
|
# include <windows.h>
|
|
# include <commdlg.h> // OPENFILENAMEW, GetOpenFileName()
|
|
# include <shlobj.h> // BROWSEINFOW, SHBrowseForFolder()
|
|
# include <FL/filename.H> // FL_EXPORT
|
|
#include <FL/platform.H> // fl_open_display
|
|
|
|
|
|
class Fl_WinAPI_Native_File_Chooser_Driver : public Fl_Native_File_Chooser_Driver {
|
|
private:
|
|
int _btype; // kind-of browser to show()
|
|
int _options; // general options
|
|
OPENFILENAMEW *_ofn_ptr; // GetOpenFileName() & GetSaveFileName() struct
|
|
BROWSEINFOW *_binf_ptr; // SHBrowseForFolder() struct
|
|
WCHAR *_wpattern; // pattern buffer for filter
|
|
char **_pathnames; // array of pathnames
|
|
int _tpathnames; // total pathnames
|
|
char *_directory; // default pathname to use
|
|
char *_title; // title for window
|
|
char *_filter; // user-side search filter
|
|
char *_parsedfilt; // filter parsed for Windows dialog
|
|
int _nfilters; // number of filters parse_filter counted
|
|
char *_preset_file; // the file to preselect
|
|
char *_errmsg; // error message
|
|
|
|
// Private methods
|
|
void errmsg(const char *msg);
|
|
|
|
void clear_pathnames();
|
|
void set_single_pathname(const char *s);
|
|
void add_pathname(const char *s);
|
|
|
|
void ClearOFN();
|
|
void ClearBINF();
|
|
void Win2Unix(char *s);
|
|
void Unix2Win(char *s);
|
|
bool IsUnixPath(const char *s);
|
|
int showfile();
|
|
int showdir();
|
|
|
|
void parse_filter(const char *);
|
|
void clear_filters();
|
|
void add_filter(const char *, const char *);
|
|
public:
|
|
Fl_WinAPI_Native_File_Chooser_Driver(int val);
|
|
~Fl_WinAPI_Native_File_Chooser_Driver();
|
|
void type(int t) FL_OVERRIDE;
|
|
int type() const FL_OVERRIDE;
|
|
void options(int o) FL_OVERRIDE;
|
|
int options() const FL_OVERRIDE;
|
|
int count() const FL_OVERRIDE;
|
|
const char *filename() const FL_OVERRIDE;
|
|
const char *filename(int i) const FL_OVERRIDE;
|
|
void directory(const char *val) FL_OVERRIDE;
|
|
const char *directory() const FL_OVERRIDE;
|
|
void title(const char *t) FL_OVERRIDE;
|
|
const char* title() const FL_OVERRIDE;
|
|
const char *filter() const FL_OVERRIDE;
|
|
void filter(const char *f) FL_OVERRIDE;
|
|
int filters() const FL_OVERRIDE;
|
|
void filter_value(int i) FL_OVERRIDE;
|
|
int filter_value() const FL_OVERRIDE;
|
|
void preset_file(const char*f) FL_OVERRIDE;
|
|
const char* preset_file() const FL_OVERRIDE;
|
|
const char *errmsg() const FL_OVERRIDE;
|
|
int show() FL_OVERRIDE;
|
|
};
|
|
|
|
|
|
Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
|
|
platform_fnfc = new Fl_WinAPI_Native_File_Chooser_Driver(val);
|
|
}
|
|
|
|
|
|
static LPCWSTR utf8towchar(const char *in);
|
|
static char *wchartoutf8(LPCWSTR in);
|
|
|
|
|
|
#define LCURLY_CHR '{'
|
|
#define RCURLY_CHR '}'
|
|
#define LBRACKET_CHR '['
|
|
#define RBRACKET_CHR ']'
|
|
|
|
// STATIC: Print Windows 'double-null' string (debug)
|
|
#ifdef DEBUG
|
|
#include <stdio.h>
|
|
static void dnullprint(char *wp) {
|
|
if ( ! wp ) return;
|
|
for ( int t=0; true; t++ ) {
|
|
if ( wp[t] == '\0' && wp[t+1] == '\0' ) {
|
|
printf("\\0\\0");
|
|
fflush(stdout);
|
|
return;
|
|
} else if ( wp[t] == '\0' ) {
|
|
printf("\\0");
|
|
} else {
|
|
printf("%c",wp[t]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Return length of double-null string
|
|
// Includes single nulls in count, excludes trailing double-null.
|
|
//
|
|
// 1234 567
|
|
// |||/\|||
|
|
// IN: "one\0two\0\0"
|
|
// OUT: 7
|
|
//
|
|
static int dnulllen(const char *wp) {
|
|
int len = 0;
|
|
while ( ! ( *(wp+0) == 0 && *(wp+1) == 0 ) ) {
|
|
++wp;
|
|
++len;
|
|
}
|
|
return(len);
|
|
}
|
|
|
|
// STATIC: Append a string to another, leaving terminated with DOUBLE NULL.
|
|
// Automatically handles extending length of string.
|
|
// wp can be NULL (a new wp will be allocated and initialized).
|
|
// string must be NULL terminated.
|
|
// The pointer wp may be modified on return.
|
|
//
|
|
static void dnullcat(char*&wp, const char *string, int n = -1 ) {
|
|
//DEBUG printf("DEBUG: dnullcat IN: <"); dnullprint(wp); printf(">\n");
|
|
size_t inlen = ( n < 0 ) ? strlen(string) : n;
|
|
char *wp2 = 0; // used to point at end of last string
|
|
if ( ! wp ) {
|
|
wp = new char[inlen + 4];
|
|
*(wp+0) = '\0';
|
|
*(wp+1) = '\0';
|
|
wp2 = wp; // no "last string", point at begin of buffer
|
|
} else {
|
|
int wplen = dnulllen(wp);
|
|
// Make copy of wp into larger buffer
|
|
char *tmp = new char[wplen + inlen + 4];
|
|
memcpy(tmp, wp, wplen+2); // copy of wp plus doublenull
|
|
delete[] wp; // delete old wp
|
|
wp = tmp; // use new copy
|
|
wp2 = wp + wplen + 1; // point at second null
|
|
//DEBUG printf("DEBUG: dnullcat COPY: <"); dnullprint(wp); printf("> (wplen=%d)\n", wplen);
|
|
}
|
|
|
|
// *wp2 points at second null; the buffer is large enough to copy the string!
|
|
strcpy(wp2, string);
|
|
*(wp2+inlen+1) = '\0'; // Leave string double-null terminated
|
|
//DEBUG printf("DEBUG: dnullcat OUT: <"); dnullprint(wp); printf(">\n\n");
|
|
}
|
|
|
|
// CTOR
|
|
Fl_WinAPI_Native_File_Chooser_Driver::Fl_WinAPI_Native_File_Chooser_Driver(int val) :
|
|
Fl_Native_File_Chooser_Driver(val) {
|
|
_btype = val;
|
|
_options = Fl_Native_File_Chooser::NO_OPTIONS;
|
|
_ofn_ptr = new OPENFILENAMEW;
|
|
_binf_ptr = new BROWSEINFOW;
|
|
_wpattern = 0;
|
|
memset((void*)_ofn_ptr, 0, sizeof(OPENFILENAMEW));
|
|
_ofn_ptr->lStructSize = sizeof(OPENFILENAMEW);
|
|
_ofn_ptr->hwndOwner = 0L;
|
|
memset((void*)_binf_ptr, 0, sizeof(BROWSEINFOW));
|
|
_pathnames = NULL;
|
|
_tpathnames = 0;
|
|
_directory = NULL;
|
|
_title = NULL;
|
|
_filter = NULL;
|
|
_parsedfilt = NULL;
|
|
_nfilters = 0;
|
|
_preset_file = NULL;
|
|
_errmsg = NULL;
|
|
}
|
|
|
|
// DTOR
|
|
Fl_WinAPI_Native_File_Chooser_Driver::~Fl_WinAPI_Native_File_Chooser_Driver() {
|
|
//_pathnames // managed by clear_pathnames()
|
|
//_tpathnames // managed by clear_pathnames()
|
|
_directory = strfree(_directory);
|
|
_title = strfree(_title);
|
|
_filter = strfree(_filter);
|
|
//_parsedfilt // managed by clear_filters()
|
|
//_nfilters // managed by clear_filters()
|
|
_preset_file = strfree(_preset_file);
|
|
_errmsg = strfree(_errmsg);
|
|
clear_filters();
|
|
clear_pathnames();
|
|
ClearOFN();
|
|
ClearBINF();
|
|
delete _binf_ptr;
|
|
delete _ofn_ptr;
|
|
if ( _wpattern ) delete[] _wpattern;
|
|
}
|
|
|
|
// SET TYPE OF BROWSER
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::type(int val) {
|
|
_btype = val;
|
|
}
|
|
|
|
// GET TYPE OF BROWSER
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::type() const {
|
|
return( _btype );
|
|
}
|
|
|
|
// SET OPTIONS
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::options(int val) {
|
|
_options = val;
|
|
}
|
|
|
|
// GET OPTIONS
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::options() const {
|
|
return(_options);
|
|
}
|
|
|
|
// PRIVATE: SET ERROR MESSAGE
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::errmsg(const char *val) {
|
|
_errmsg = strfree(_errmsg);
|
|
_errmsg = strnew(val);
|
|
}
|
|
|
|
// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::clear_pathnames() {
|
|
if ( _pathnames ) {
|
|
while ( --_tpathnames >= 0 ) {
|
|
_pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]);
|
|
}
|
|
delete[] _pathnames;
|
|
_pathnames = NULL;
|
|
}
|
|
_tpathnames = 0;
|
|
}
|
|
|
|
// SET A SINGLE PATHNAME
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::set_single_pathname(const char *s) {
|
|
clear_pathnames();
|
|
_pathnames = new char*[1];
|
|
_pathnames[0] = strnew(s);
|
|
_tpathnames = 1;
|
|
}
|
|
|
|
// ADD PATHNAME TO EXISTING ARRAY
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::add_pathname(const char *s) {
|
|
if ( ! _pathnames ) {
|
|
// Create first element in array
|
|
++_tpathnames;
|
|
_pathnames = new char*[_tpathnames];
|
|
} else {
|
|
// Grow array by 1
|
|
char **tmp = new char*[_tpathnames+1]; // create new buffer
|
|
memcpy((void*)tmp, (void*)_pathnames,
|
|
sizeof(char*)*_tpathnames); // copy old
|
|
delete[] _pathnames; // delete old
|
|
_pathnames = tmp; // use new
|
|
++_tpathnames;
|
|
}
|
|
_pathnames[_tpathnames-1] = strnew(s);
|
|
}
|
|
|
|
// FREE A PIDL (Pointer to IDentity List)
|
|
static void FreePIDL(LPITEMIDLIST pidl) {
|
|
IMalloc *imalloc = NULL;
|
|
if ( SUCCEEDED(SHGetMalloc(&imalloc)) ) {
|
|
imalloc->Free(pidl);
|
|
imalloc->Release();
|
|
imalloc = NULL;
|
|
}
|
|
}
|
|
|
|
// CLEAR MICROSOFT OFN (OPEN FILE NAME) CLASS
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::ClearOFN() {
|
|
// Free any previously allocated lpstrFile before zeroing out _ofn_ptr
|
|
if ( _ofn_ptr->lpstrFile ) {
|
|
delete[] _ofn_ptr->lpstrFile;
|
|
_ofn_ptr->lpstrFile = NULL;
|
|
}
|
|
if ( _ofn_ptr->lpstrInitialDir ) {
|
|
delete[] (TCHAR*) _ofn_ptr->lpstrInitialDir; //msvc6 compilation fix
|
|
_ofn_ptr->lpstrInitialDir = NULL;
|
|
}
|
|
_ofn_ptr->lpstrFilter = NULL; // (deleted elsewhere)
|
|
int temp = _ofn_ptr->nFilterIndex; // keep the filter_value
|
|
memset((void*)_ofn_ptr, 0, sizeof(OPENFILENAMEW));
|
|
_ofn_ptr->lStructSize = sizeof(OPENFILENAMEW);
|
|
_ofn_ptr->nFilterIndex = temp;
|
|
}
|
|
|
|
// CLEAR MICROSOFT BINF (BROWSER INFO) CLASS
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::ClearBINF() {
|
|
if ( _binf_ptr->pidlRoot ) {
|
|
FreePIDL((ITEMIDLIST*)_binf_ptr->pidlRoot);
|
|
_binf_ptr->pidlRoot = NULL;
|
|
}
|
|
memset((void*)_binf_ptr, 0, sizeof(BROWSEINFOW));
|
|
}
|
|
|
|
// CONVERT WINDOWS BACKSLASHES TO UNIX FRONTSLASHES
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::Win2Unix(char *s) {
|
|
while ( (s=strchr(s,'\\')) ) *s = '/';
|
|
}
|
|
|
|
// CONVERT UNIX FRONTSLASHES TO WINDOWS BACKSLASHES
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::Unix2Win(char *s) {
|
|
while ( (s=strchr(s,'/')) ) *s = '\\';
|
|
}
|
|
|
|
// SEE IF PATH IS FRONT SLASH OR BACKSLASH STYLE
|
|
// Use this to preserve path style after windows dialog appears.
|
|
// If no slashes are specified, windows is assumed.
|
|
// If a mix of both path styles is used, windows is assumed.
|
|
//
|
|
bool Fl_WinAPI_Native_File_Chooser_Driver::IsUnixPath(const char *s) {
|
|
if ( !s ) return false; // NULL?
|
|
if ( strchr(s, '\\') ) return false; // windows style?
|
|
if ( strchr(s, '/') ) return true; // unix style?
|
|
return false; // no slashes? assume native windows
|
|
}
|
|
|
|
// SAVE THE CURRENT WORKING DIRECTORY
|
|
// Returns a malloc()ed copy of the cwd that can
|
|
// later be freed with RestoreCWD(). May return 0 on error.
|
|
//
|
|
static char *SaveCWD() {
|
|
char *thecwd = 0;
|
|
DWORD thecwdsz = GetCurrentDirectory(0,0);
|
|
if ( thecwdsz > 0 ) {
|
|
thecwd = (char*)malloc(thecwdsz);
|
|
if (GetCurrentDirectory(thecwdsz, thecwd) == 0 ) {
|
|
free(thecwd); thecwd = 0;
|
|
}
|
|
}
|
|
return thecwd;
|
|
}
|
|
|
|
// RESTORES THE CWD SAVED BY SaveCWD(), FREES STRING
|
|
// Always returns NULL (string was freed).
|
|
//
|
|
static void RestoreCWD(char *thecwd) {
|
|
if ( !thecwd ) return;
|
|
SetCurrentDirectory(thecwd);
|
|
free(thecwd);
|
|
}
|
|
|
|
// SHOW FILE BROWSER
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::showfile() {
|
|
bool unixpath = IsUnixPath(_directory) || IsUnixPath(_preset_file); // caller uses unix paths?
|
|
ClearOFN();
|
|
clear_pathnames();
|
|
size_t fsize = FNFC_MAX_PATH;
|
|
_ofn_ptr->Flags |= OFN_NOVALIDATE; // prevent disabling of front slashes
|
|
_ofn_ptr->Flags |= OFN_HIDEREADONLY; // hide goofy readonly flag
|
|
// USE NEW BROWSER
|
|
_ofn_ptr->Flags |= OFN_EXPLORER; // use newer explorer windows
|
|
_ofn_ptr->Flags |= OFN_ENABLESIZING; // allow window to be resized (hey, why not?)
|
|
_ofn_ptr->Flags |= OFN_NOCHANGEDIR; // XXX: docs say ineffective on XP/2K/NT, but set it anyway..
|
|
|
|
switch ( _btype ) {
|
|
case Fl_Native_File_Chooser::BROWSE_DIRECTORY:
|
|
case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY:
|
|
case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY:
|
|
abort(); // never happens: handled by showdir()
|
|
case Fl_Native_File_Chooser::BROWSE_FILE:
|
|
break;
|
|
case Fl_Native_File_Chooser::BROWSE_MULTI_FILE:
|
|
_ofn_ptr->Flags |= OFN_ALLOWMULTISELECT;
|
|
break;
|
|
case Fl_Native_File_Chooser::BROWSE_SAVE_FILE:
|
|
if ( options() & Fl_Native_File_Chooser::SAVEAS_CONFIRM && type() == Fl_Native_File_Chooser::BROWSE_SAVE_FILE ) {
|
|
_ofn_ptr->Flags |= OFN_OVERWRITEPROMPT;
|
|
}
|
|
break;
|
|
}
|
|
// SPACE FOR RETURNED FILENAME
|
|
_ofn_ptr->lpstrFile = new WCHAR[fsize];
|
|
_ofn_ptr->nMaxFile = (DWORD)(fsize-1);
|
|
_ofn_ptr->lpstrFile[0] = 0;
|
|
_ofn_ptr->lpstrFile[1] = 0; // dnull
|
|
// PARENT WINDOW
|
|
_ofn_ptr->hwndOwner = GetForegroundWindow();
|
|
// DIALOG TITLE
|
|
if (_title) {
|
|
static WCHAR wtitle[200];
|
|
wcsncpy(wtitle, utf8towchar(_title), 200);
|
|
wtitle[200-1] = 0;
|
|
_ofn_ptr->lpstrTitle = wtitle;
|
|
} else {
|
|
_ofn_ptr->lpstrTitle = NULL;
|
|
}
|
|
// FILTER
|
|
if (_parsedfilt != NULL) { // to convert a null-containing char string into a widechar string
|
|
// NEW
|
|
if ( !_wpattern ) _wpattern = new WCHAR[FNFC_MAX_PATH];
|
|
const char *p = _parsedfilt;
|
|
while(*(p + strlen(p) + 1) != 0) p += strlen(p) + 1;
|
|
p += strlen(p) + 2;
|
|
MultiByteToWideChar(CP_UTF8, 0, _parsedfilt, (int) (p - _parsedfilt), _wpattern, FNFC_MAX_PATH);
|
|
_ofn_ptr->lpstrFilter = _wpattern;
|
|
} else {
|
|
_ofn_ptr->lpstrFilter = NULL;
|
|
}
|
|
// PRESET FILE
|
|
// If set, supercedes _directory. See KB Q86920 for details
|
|
// XXX: this doesn't preselect the item in the listview.. why?
|
|
//
|
|
if ( _preset_file ) {
|
|
// Temp copy of _dirname we can convert to windows path if needed
|
|
char *winpath = fl_strdup(_preset_file);
|
|
if ( unixpath ) Unix2Win(winpath);
|
|
size_t len = strlen(winpath);
|
|
if ( len >= _ofn_ptr->nMaxFile ) {
|
|
char msg[80];
|
|
snprintf(msg, 80, "preset_file() filename is too long: %ld is >=%ld", (long)len, (long)fsize);
|
|
errmsg(msg);
|
|
return(-1);
|
|
}
|
|
wcscpy(_ofn_ptr->lpstrFile, utf8towchar(winpath));
|
|
len = wcslen(_ofn_ptr->lpstrFile);
|
|
_ofn_ptr->lpstrFile[len+0] = 0; // multiselect needs dnull
|
|
_ofn_ptr->lpstrFile[len+1] = 0;
|
|
free(winpath); // free temp copy now that we have a new wchar
|
|
//wprintf(L"lpstrFile is '%ls'\n", (WCHAR*)(_ofn_ptr->lpstrFile));
|
|
}
|
|
// PRESET DIR
|
|
// XXX: See KB Q86920 for doc bug:
|
|
// http://support.microsoft.com/default.aspx?scid=kb;en-us;86920
|
|
//
|
|
if ( _directory ) {
|
|
// Temp copy of _dirname we can convert to windows path if needed
|
|
char *winpath = fl_strdup(_directory);
|
|
// Caller specified unix front slash path?
|
|
// If so, convert to backslashes; windows native browser mishandles unix style paths.
|
|
// We'll convert back to unix style when dialog completes.
|
|
//
|
|
if ( unixpath ) Unix2Win(winpath);
|
|
// Make a wide char version of potentially utf8 string
|
|
_ofn_ptr->lpstrInitialDir = new WCHAR[FNFC_MAX_PATH];
|
|
wcscpy((WCHAR *)_ofn_ptr->lpstrInitialDir, utf8towchar(winpath));
|
|
free(winpath); // free temp copy now that we have a new wchar
|
|
//wprintf(L"lpstrInitialDir is '%ls'\n", (WCHAR*)(_ofn_ptr->lpstrInitialDir));
|
|
}
|
|
// SAVE THE CURRENT DIRECTORY
|
|
// See above warning (XXX) for OFN_NOCHANGEDIR
|
|
//
|
|
char *save_cwd = SaveCWD(); // must be freed with RestoreCWD()
|
|
// OPEN THE DIALOG WINDOW
|
|
int err;
|
|
if ( _btype == Fl_Native_File_Chooser::BROWSE_SAVE_FILE ) {
|
|
err = GetSaveFileNameW(_ofn_ptr);
|
|
} else {
|
|
err = GetOpenFileNameW(_ofn_ptr);
|
|
}
|
|
// GET EXTENDED ERROR
|
|
int exterr = CommDlgExtendedError();
|
|
// RESTORE CURRENT DIRECTORY
|
|
RestoreCWD(save_cwd); save_cwd = 0; // also frees save_cwd
|
|
// ERROR OR CANCEL?
|
|
if ( err == 0 ) {
|
|
if ( exterr == 0 ) return(1); // user hit cancel
|
|
// Otherwise, an error occurred..
|
|
char msg[80];
|
|
snprintf(msg, 80, "CommDlgExtendedError() code=%d", exterr);
|
|
errmsg(msg);
|
|
return(-1);
|
|
}
|
|
// PREPARE PATHNAMES FOR RETURN
|
|
switch ( _btype ) {
|
|
case Fl_Native_File_Chooser::BROWSE_FILE:
|
|
case Fl_Native_File_Chooser::BROWSE_SAVE_FILE:
|
|
set_single_pathname(wchartoutf8(_ofn_ptr->lpstrFile));
|
|
if ( unixpath ) Win2Unix(_pathnames[_tpathnames-1]); // preserve unix style path
|
|
break;
|
|
case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: {
|
|
// EXTRACT MULTIPLE FILENAMES
|
|
const WCHAR *dirname = _ofn_ptr->lpstrFile;
|
|
size_t dirlen = wcslen(dirname);
|
|
if ( dirlen > 0 ) {
|
|
// WALK STRING SEARCHING FOR 'DOUBLE-NULL'
|
|
// eg. "/dir/name\0foo1\0foo2\0foo3\0\0"
|
|
//
|
|
char pathname[FNFC_MAX_PATH];
|
|
for ( const WCHAR *s = dirname + dirlen + 1; *s; s += (wcslen(s)+1)) {
|
|
// ISSUE #206: replace strncpy/cat with fl_strlcpy/cat
|
|
fl_strlcpy(pathname, wchartoutf8(dirname), FNFC_MAX_PATH);
|
|
fl_strlcat(pathname, "\\", FNFC_MAX_PATH);
|
|
fl_strlcat(pathname, wchartoutf8(s), FNFC_MAX_PATH);
|
|
add_pathname(pathname);
|
|
}
|
|
}
|
|
// XXX
|
|
// Work around problem where pasted forward-slash pathname
|
|
// into the file browser causes new "Explorer" interface
|
|
// not to grok forward slashes, passing back as a 'filename'..!
|
|
//
|
|
if ( _tpathnames == 0 ) {
|
|
add_pathname(wchartoutf8(dirname));
|
|
}
|
|
// Caller specified unix path? Return unix paths
|
|
if ( unixpath ) {
|
|
for ( int t=0; t<_tpathnames; t++ ) {
|
|
Win2Unix(_pathnames[t]);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Fl_Native_File_Chooser::BROWSE_DIRECTORY:
|
|
case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY:
|
|
case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY:
|
|
abort(); // never happens: handled by showdir()
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
// Used by SHBrowseForFolder(), sets initial selected dir.
|
|
// Ref: Usenet: microsoft.public.vc.mfc, Dec 8 2000, 1:38p David Lowndes
|
|
// Subject: How to specify to select an initial folder .."
|
|
//
|
|
static int CALLBACK Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data) {
|
|
switch (msg) {
|
|
case BFFM_INITIALIZED:
|
|
if (data) ::SendMessageW(win, BFFM_SETSELECTIONW, TRUE, data);
|
|
break;
|
|
case BFFM_SELCHANGED:
|
|
TCHAR path[FNFC_MAX_PATH];
|
|
if ( SHGetPathFromIDList((ITEMIDLIST*)param, path) ) {
|
|
::SendMessage(win, BFFM_ENABLEOK, 0, 1);
|
|
} else {
|
|
// disable ok button if not a path
|
|
::SendMessage(win, BFFM_ENABLEOK, 0, 0);
|
|
}
|
|
break;
|
|
case BFFM_VALIDATEFAILED:
|
|
// we could pop up an annoying message here.
|
|
// also needs set ulFlags |= BIF_VALIDATE
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
// SHOW DIRECTORY BROWSER
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::showdir() {
|
|
bool unixpath = IsUnixPath(_directory); // caller uses unix paths?
|
|
// initialize OLE only once
|
|
fl_open_display(); // init needed by BIF_USENEWUI
|
|
ClearBINF();
|
|
clear_pathnames();
|
|
// PARENT WINDOW
|
|
_binf_ptr->hwndOwner = GetForegroundWindow();
|
|
// DIALOG TITLE
|
|
//_binf_ptr->lpszTitle = _title ? _title : NULL;
|
|
if (_title) {
|
|
static WCHAR wtitle[256];
|
|
wcsncpy(wtitle, utf8towchar(_title), 256);
|
|
wtitle[255] = 0;
|
|
_binf_ptr->lpszTitle = wtitle;
|
|
} else {
|
|
_binf_ptr->lpszTitle = NULL;
|
|
}
|
|
|
|
// FLAGS
|
|
_binf_ptr->ulFlags = 0; // initialize
|
|
|
|
// TBD: make sure matches to runtime system, if need be.
|
|
//(what if _WIN32_IE doesn't match system? does the program not run?)
|
|
//
|
|
// TBD: match all 3 types of directories
|
|
//
|
|
// NOTE: *Don't* use BIF_SHAREABLE. It /disables/ mapped network shares
|
|
// from being visible in BROWSE_DIRECTORY mode. Walter Garm's comments:
|
|
//
|
|
// --- Garms, Walter (GE EntSol, Security) wrote:
|
|
// With your help I was able to solve the problem of the network drives.
|
|
// For Version 6.0, at least, the BIF_SHAREABLE flag seems to have the
|
|
// opposite sense: With BIF_SHAREABLE not set I see the mapped network
|
|
// drives, and with BIF_SHAREABLE set I do not.
|
|
// ---
|
|
|
|
#if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0
|
|
if ( _btype == Fl_Native_File_Chooser::BROWSE_DIRECTORY ) _binf_ptr->ulFlags |= BIF_NONEWFOLDERBUTTON;
|
|
_binf_ptr->ulFlags |= BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
|
|
#elif defined(BIF_USENEWUI) // Version 5.0
|
|
if ( _btype == Fl_Native_File_Chooser::BROWSE_DIRECTORY ) _binf_ptr->ulFlags |= BIF_EDITBOX;
|
|
else if ( _btype == Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY ) _binf_ptr->ulFlags |= BIF_USENEWUI;
|
|
_binf_ptr->ulFlags |= BIF_RETURNONLYFSDIRS;
|
|
#elif defined(BIF_EDITBOX) // Version 4.71
|
|
_binf_ptr->ulFlags |= BIF_RETURNONLYFSDIRS | BIF_EDITBOX;
|
|
#else // Version Old
|
|
_binf_ptr->ulFlags |= BIF_RETURNONLYFSDIRS;
|
|
#endif
|
|
|
|
// BUFFER
|
|
//char displayname[FNFC_MAX_PATH];
|
|
WCHAR displayname[FNFC_MAX_PATH];
|
|
_binf_ptr->pszDisplayName = displayname;
|
|
|
|
// PRESET DIR
|
|
WCHAR presetname[FNFC_MAX_PATH];
|
|
if ( _directory ) {
|
|
// Temp copy of _dirname we can convert to windows path if needed
|
|
char *winpath = fl_strdup(_directory);
|
|
// Caller specified unix front slash path?
|
|
// If so, convert to backslashes; windows native browser mishandles unix style paths.
|
|
// We'll convert back to unix style when dialog completes.
|
|
//
|
|
if ( unixpath ) Unix2Win(winpath);
|
|
// Wide char version of potentially utf8 string
|
|
wcsncpy(presetname, utf8towchar(winpath), FNFC_MAX_PATH);
|
|
free(winpath); // free temp copy now that we have a new wchar
|
|
presetname[FNFC_MAX_PATH-1] = 0; // dnull
|
|
presetname[FNFC_MAX_PATH-2] = 0;
|
|
_binf_ptr->lParam = (LPARAM)presetname;
|
|
//wprintf(L"presetname is '%ls'\n", (WCHAR*)(presetname));
|
|
}
|
|
else _binf_ptr->lParam = 0;
|
|
_binf_ptr->lpfn = Dir_CB;
|
|
// OPEN BROWSER
|
|
LPITEMIDLIST pidl = SHBrowseForFolderW(_binf_ptr);
|
|
// CANCEL?
|
|
if ( pidl == NULL ) return(1);
|
|
|
|
// GET THE PATHNAME(S) THE USER SELECTED
|
|
// TBD: expand NetHood shortcuts from this PIDL??
|
|
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shbrowseforfolder.asp
|
|
|
|
WCHAR path[FNFC_MAX_PATH];
|
|
if ( SHGetPathFromIDListW(pidl, path) ) {
|
|
add_pathname(wchartoutf8(path));
|
|
if ( unixpath ) Win2Unix(_pathnames[_tpathnames-1]); // preserve unix style path
|
|
}
|
|
FreePIDL(pidl);
|
|
if ( !wcslen(path) ) return(1); // don't return empty pathnames
|
|
return(0);
|
|
}
|
|
|
|
// RETURNS:
|
|
// 0 - user picked a file
|
|
// 1 - user cancelled
|
|
// -1 - failed; errmsg() has reason
|
|
//
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::show() {
|
|
int retval;
|
|
if ( _btype == Fl_Native_File_Chooser::BROWSE_DIRECTORY ||
|
|
_btype == Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY ||
|
|
_btype == Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY ) {
|
|
retval = showdir();
|
|
} else {
|
|
retval = showfile();
|
|
}
|
|
// restore the correct state of mouse buttons and keyboard modifier keys (STR #3221)
|
|
HWND h = GetForegroundWindow();
|
|
if (h) {
|
|
WNDPROC windproc = (WNDPROC)GetWindowLongPtrW(h, GWLP_WNDPROC);
|
|
CallWindowProc(windproc, h, WM_ACTIVATEAPP, 1, 0);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
// RETURN ERROR MESSAGE
|
|
const char *Fl_WinAPI_Native_File_Chooser_Driver::errmsg() const {
|
|
return(_errmsg ? _errmsg : "No error");
|
|
}
|
|
|
|
// GET FILENAME
|
|
const char* Fl_WinAPI_Native_File_Chooser_Driver::filename() const {
|
|
if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]);
|
|
return("");
|
|
}
|
|
|
|
// GET FILENAME FROM LIST OF FILENAMES
|
|
const char* Fl_WinAPI_Native_File_Chooser_Driver::filename(int i) const {
|
|
if ( _pathnames && i < _tpathnames ) return(_pathnames[i]);
|
|
return("");
|
|
}
|
|
|
|
// GET TOTAL FILENAMES CHOSEN
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::count() const {
|
|
return(_tpathnames);
|
|
}
|
|
|
|
// PRESET PATHNAME
|
|
// Can be NULL if no preset is desired.
|
|
//
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::directory(const char *val) {
|
|
_directory = strfree(_directory);
|
|
_directory = strnew(val);
|
|
}
|
|
|
|
// GET PRESET PATHNAME
|
|
// Can return NULL if none set.
|
|
//
|
|
const char *Fl_WinAPI_Native_File_Chooser_Driver::directory() const {
|
|
return(_directory);
|
|
}
|
|
|
|
// SET TITLE
|
|
// Can be NULL if no title desired.
|
|
//
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::title(const char *val) {
|
|
_title = strfree(_title);
|
|
_title = strnew(val);
|
|
}
|
|
|
|
// GET TITLE
|
|
// Can return NULL if none set.
|
|
//
|
|
const char *Fl_WinAPI_Native_File_Chooser_Driver::title() const {
|
|
return(_title);
|
|
}
|
|
|
|
// SET FILTER
|
|
// Can be NULL if no filter needed
|
|
//
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::filter(const char *val) {
|
|
_filter = strfree(_filter);
|
|
clear_filters();
|
|
if ( val ) {
|
|
_filter = strnew(val);
|
|
parse_filter(_filter);
|
|
}
|
|
add_filter("All Files", "*.*"); // always include 'all files' option
|
|
|
|
#ifdef DEBUG
|
|
dnullprint(_parsedfilt);
|
|
#endif /*DEBUG*/
|
|
}
|
|
|
|
// GET FILTER
|
|
// Can return NULL if none set.
|
|
//
|
|
const char *Fl_WinAPI_Native_File_Chooser_Driver::filter() const {
|
|
return(_filter);
|
|
}
|
|
|
|
// CLEAR FILTERS
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::clear_filters() {
|
|
_nfilters = 0;
|
|
_parsedfilt = strfree(_parsedfilt);
|
|
}
|
|
|
|
// ADD A FILTER
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::add_filter(const char *name_in, // name of filter (optional: can be null)
|
|
const char *winfilter) { // windows style filter (eg. "*.cxx;*.h")
|
|
// No name? Make one..
|
|
char name[1024];
|
|
if ( !name_in || name_in[0] == '\0' ) {
|
|
snprintf(name, sizeof(name), "%.*s Files", int(sizeof(name)-10), winfilter);
|
|
} else {
|
|
if ((strlen(name_in)+strlen(winfilter)+3) < sizeof(name)) {
|
|
snprintf(name, sizeof(name), "%s (%s)", name_in, winfilter);
|
|
} else {
|
|
snprintf(name, sizeof(name), "%.*s", int(sizeof(name))-1, name_in);
|
|
}
|
|
}
|
|
dnullcat(_parsedfilt, name);
|
|
dnullcat(_parsedfilt, winfilter);
|
|
_nfilters++;
|
|
//DEBUG printf("DEBUG: ADD FILTER name=<%s> winfilter=<%s>\n", name, winfilter);
|
|
}
|
|
|
|
// RETURN HOW MANY DIFFERENT FILTERS WERE SPECIFIED
|
|
// In: "foo.[CH]" or "foo.{C,H}"
|
|
// Out: 2
|
|
//
|
|
static int count_filters(const char *filter) {
|
|
int count = 0;
|
|
char mode = 0;
|
|
const char *in = filter;
|
|
while (*in) {
|
|
switch(*in) {
|
|
case '\\': // escape next character
|
|
++in; if ( *in == 0 ) continue; // skip escape. EOL? done
|
|
++in; // skip escaped char
|
|
continue;
|
|
case LCURLY_CHR: // start "{aaa,bbb}"
|
|
mode = *in; // set mode, parse over curly
|
|
++count; // at least +1 wildcard
|
|
break;
|
|
case RCURLY_CHR: // end "{aaa,bbb}"
|
|
if ( mode == LCURLY_CHR ) // disable curly mode (if on)
|
|
mode = 0;
|
|
break;
|
|
case LBRACKET_CHR: // start "[xyz]"
|
|
mode = *in; // set mode, parse over bracket
|
|
break;
|
|
case RBRACKET_CHR: // end "[xyz]"
|
|
if ( mode == LBRACKET_CHR ) // disable bracket mode (if on)
|
|
mode = 0;
|
|
break;
|
|
default: // any other char
|
|
switch (mode) { // handle {} or [] modes
|
|
case LCURLY_CHR: // handle "{aaa,bbb}"
|
|
if (*in==',' || *in=='|') // ',' and '|' adds filters
|
|
++count;
|
|
break;
|
|
case LBRACKET_CHR: // handle "[xyz]"
|
|
++count; // all chars in []'s add new filter
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
++in; // parse past char
|
|
}
|
|
return count > 0 ? count : 1; // return at least 1
|
|
}
|
|
|
|
// Convert FLTK style pattern matches to windows 'double-null' pattern
|
|
// Returns with the parsed double-null result in '_parsedfilt'.
|
|
//
|
|
// Handles:
|
|
// IN OUT
|
|
// ----------- -----------------------------
|
|
// *.{ma,mb} "*.{ma,mb} Files\0*.ma;*.mb\0\0"
|
|
// *.[abc] "*.[abc] Files\0*.a;*.b;*.c\0\0"
|
|
// *.txt "*.txt Files\0*.txt\0\0"
|
|
// C Files\t*.[ch] "C Files\0*.c;*.h\0\0"
|
|
//
|
|
// Example:
|
|
// IN: "*.{ma,mb}"
|
|
// OUT: "*.ma;*.mb Files\0*.ma;*.mb\0All Files\0*.*\0\0"
|
|
// --------------- --------- --------- ---
|
|
// | | | |
|
|
// Title Wildcards Title Wildcards
|
|
//
|
|
// Parsing Mode:
|
|
// IN:"C Files\t*.{cxx,h}"
|
|
// ||||||| |||||||||
|
|
// mode: nnnnnnn ww{{{{{{{
|
|
// \_____/ \_______/
|
|
// Name Wildcard
|
|
//
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::parse_filter(const char *in) {
|
|
clear_filters();
|
|
if ( ! in || in[0] == '\0' ) return;
|
|
|
|
int has_name = strchr(in, '\t') ? 1 : 0;
|
|
char mode = has_name ? 'n' : 'w'; // parse mode: n=name, w=wildcard
|
|
|
|
// whatever input string is, our output won't be much longer in length..
|
|
// use double length just for safety.
|
|
size_t slen = strlen(in);
|
|
char *wildprefix = new char[(slen+1)*2]; wildprefix[0] = 0;
|
|
char *comp = new char[(slen+1)*2]; comp[0] = 0;
|
|
char *name = new char[(slen+1)*2]; name[0] = 0;
|
|
|
|
// Init
|
|
int nwildcards = 0;
|
|
int maxfilters = count_filters(in) + 1; // count wildcard seps
|
|
char **wildcards = new char*[maxfilters]; // parsed wildcards (can be several)
|
|
int t;
|
|
for ( t=0; t<maxfilters; t++ ) {
|
|
wildcards[t] = new char[slen+1];
|
|
wildcards[t][0] = '\0';
|
|
}
|
|
|
|
// Parse
|
|
for ( ; 1; in++ ) {
|
|
|
|
// DEBUG
|
|
// printf("WORKING ON '%c': mode=<%c> name=<%s> wildprefix=<%s> nwildcards=%d wildcards[n]=<%s>\n",
|
|
// *in, mode, name, wildprefix, nwildcards, wildcards[nwildcards]);
|
|
|
|
switch (*in) {
|
|
case ',':
|
|
case '|':
|
|
if ( mode == LCURLY_CHR ) {
|
|
// create new wildcard, copy in prefix
|
|
strcat(wildcards[nwildcards++], wildprefix);
|
|
continue;
|
|
} else {
|
|
goto regchar;
|
|
}
|
|
continue;
|
|
|
|
// FINISHED PARSING A NAME?
|
|
case '\t':
|
|
if ( mode != 'n' ) goto regchar;
|
|
// finish parsing name? switch to wildcard mode
|
|
mode = 'w';
|
|
break;
|
|
|
|
// ESCAPE NEXT CHAR
|
|
case '\\':
|
|
++in;
|
|
goto regchar;
|
|
|
|
// FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
|
|
case '\r':
|
|
case '\n':
|
|
case '\0':
|
|
{
|
|
if ( mode == 'w' ) { // finished parsing wildcard?
|
|
if ( nwildcards == 0 ) {
|
|
strcpy(wildcards[nwildcards++], wildprefix);
|
|
}
|
|
// Append wildcards in Microsoft's "*.one;*.two" format
|
|
comp[0] = 0;
|
|
for ( t=0; t<nwildcards; t++ ) {
|
|
if ( t != 0 ) strcat(comp, ";");
|
|
strcat(comp, wildcards[t]);
|
|
}
|
|
// Add if not empty
|
|
if ( comp[0] ) {
|
|
add_filter(name, comp);
|
|
}
|
|
}
|
|
// RESET
|
|
for ( t=0; t<maxfilters; t++ ) {
|
|
wildcards[t][0] = '\0';
|
|
}
|
|
nwildcards = 0;
|
|
wildprefix[0] = name[0] = '\0';
|
|
mode = strchr(in,'\t') ? 'n' : 'w';
|
|
// DONE?
|
|
if ( *in == '\0' ) { // done
|
|
// Free everything
|
|
delete[] wildprefix;
|
|
delete[] comp;
|
|
delete[] name;
|
|
for ( t=0; t<maxfilters; t++ ) delete[] wildcards[t];
|
|
delete[] wildcards;
|
|
return;
|
|
}
|
|
continue; // not done yet, more filters
|
|
}
|
|
|
|
// STARTING A WILDCARD?
|
|
case LBRACKET_CHR:
|
|
case LCURLY_CHR:
|
|
mode = *in;
|
|
if ( *in == LCURLY_CHR ) {
|
|
// create new wildcard
|
|
strcat(wildcards[nwildcards++], wildprefix);
|
|
}
|
|
continue;
|
|
|
|
// ENDING A WILDCARD?
|
|
case RBRACKET_CHR:
|
|
case RCURLY_CHR:
|
|
mode = 'w'; // back to wildcard mode
|
|
continue;
|
|
|
|
// ALL OTHER NON-SPECIAL CHARACTERS
|
|
default:
|
|
regchar: // handle regular char
|
|
switch ( mode ) {
|
|
case LBRACKET_CHR:
|
|
// create new wildcard
|
|
++nwildcards;
|
|
// copy in prefix
|
|
strcpy(wildcards[nwildcards-1], wildprefix);
|
|
// append search char
|
|
chrcat(wildcards[nwildcards-1], *in);
|
|
continue;
|
|
|
|
case LCURLY_CHR:
|
|
if ( nwildcards > 0 ) {
|
|
chrcat(wildcards[nwildcards-1], *in);
|
|
}
|
|
continue;
|
|
|
|
case 'n':
|
|
chrcat(name, *in);
|
|
continue;
|
|
|
|
case 'w':
|
|
chrcat(wildprefix, *in);
|
|
for ( t=0; t<nwildcards; t++ ) {
|
|
chrcat(wildcards[t], *in);
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// SET 'CURRENTLY SELECTED FILTER'
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::filter_value(int i) {
|
|
_ofn_ptr->nFilterIndex = i + 1;
|
|
}
|
|
|
|
// RETURN VALUE OF 'CURRENTLY SELECTED FILTER'
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::filter_value() const {
|
|
return(_ofn_ptr->nFilterIndex ? _ofn_ptr->nFilterIndex-1 : _nfilters+1);
|
|
}
|
|
|
|
// PRESET FILENAME FOR 'SAVE AS' CHOOSER
|
|
void Fl_WinAPI_Native_File_Chooser_Driver::preset_file(const char* val) {
|
|
_preset_file = strfree(_preset_file);
|
|
_preset_file = strnew(val);
|
|
}
|
|
|
|
// GET PRESET FILENAME FOR 'SAVE AS' CHOOSER
|
|
const char* Fl_WinAPI_Native_File_Chooser_Driver::preset_file() const {
|
|
return(_preset_file);
|
|
}
|
|
|
|
int Fl_WinAPI_Native_File_Chooser_Driver::filters() const {
|
|
return(_nfilters);
|
|
}
|
|
|
|
static char *wchartoutf8(LPCWSTR in) {
|
|
static char *out = NULL;
|
|
static int lchar = 0;
|
|
if (in == NULL)return NULL;
|
|
int utf8len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
|
|
if (utf8len > lchar) {
|
|
lchar = utf8len;
|
|
out = (char *)realloc(out, lchar * sizeof(char));
|
|
}
|
|
WideCharToMultiByte(CP_UTF8, 0, in, -1, out, utf8len, NULL, NULL);
|
|
return out;
|
|
}
|
|
|
|
static LPCWSTR utf8towchar(const char *in) {
|
|
static WCHAR *wout = NULL;
|
|
static int lwout = 0;
|
|
if (in == NULL)return NULL;
|
|
int wlen = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
|
|
if (wlen > lwout) {
|
|
lwout = wlen;
|
|
wout = (WCHAR *)realloc(wout, lwout * sizeof(WCHAR));
|
|
}
|
|
MultiByteToWideChar(CP_UTF8, 0, in, -1, wout, wlen);
|
|
return wout;
|
|
}
|
|
|
|
#endif /* !FL_DOXYGEN */
|