From 5f72cbb10319db5e68ca210013cfddaf3d83d770 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 12 May 2011 18:22:51 +0200 Subject: [PATCH] Add support for flushing messages from plugin to viewer. Actually flush messages before terminating a plugin (upon the shutdown message) and flush messages in the file- and dirpicker before opening the blocking dialog. Flush debug messages too (deeper into the code, just prior to the actual blocking call). Also, fix the context folder map to be a thread-safe singleton and *attempt* to add support for default folders to windows and Mac. The latter might even not compile yet and definitely have to be tested (and fixed): Opening a DirPicker in preferences --> Network and Set the directory location of the cache. It should open a Dialog window where you are already in the folder that is the current cache directory setting (you can click Cancel after verifying that this worked). And, start to upload an image, select a file is some directory (other than what it starts in). You can omit the actual upload by clicking cancel in the preview. Then upload again and now it should start in the same folder as that you were just in. Possibly you need to first open a file picker elsewhere with a different context though, or windows might choose to open in the last folder anyway while the code doesn't really work. Uploading a sound before the second texture upload should do the trick. --- indra/llplugin/llpluginmessagepipe.cpp | 49 +++++-- indra/llplugin/llpluginmessagepipe.h | 6 +- indra/llplugin/llpluginprocesschild.cpp | 11 ++ indra/llplugin/llpluginprocessparent.cpp | 4 + indra/llplugin/llpluginprocessparent.h | 3 +- indra/newview/statemachine/aifilepicker.cpp | 24 +++- indra/newview/statemachine/aifilepicker.h | 7 +- .../plugins/base_basic/basic_plugin_base.cpp | 17 +++ indra/plugins/base_basic/basic_plugin_base.h | 13 +- .../filepicker/basic_plugin_filepicker.cpp | 6 +- indra/plugins/filepicker/legacy.cpp | 2 +- indra/plugins/filepicker/lldirpicker.cpp | 72 ++++++---- indra/plugins/filepicker/lldirpicker.h | 2 +- indra/plugins/filepicker/llfilepicker.cpp | 124 +++++++++++++----- indra/plugins/filepicker/llfilepicker.h | 9 +- 15 files changed, 258 insertions(+), 91 deletions(-) diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp index ebac3c52b..7aaa09372 100644 --- a/indra/llplugin/llpluginmessagepipe.cpp +++ b/indra/llplugin/llpluginmessagepipe.cpp @@ -89,6 +89,16 @@ bool LLPluginMessagePipeOwner::writeMessageRaw(const std::string &message) return result; } +bool LLPluginMessagePipeOwner::flushMessages(void) +{ + bool result = true; + if (mMessagePipe != NULL) + { + result = mMessagePipe->flushMessages(); + } + return result; +} + void LLPluginMessagePipeOwner::killMessagePipe(void) { if(mMessagePipe != NULL) @@ -163,26 +173,32 @@ bool LLPluginMessagePipe::pump(F64 timeout) return result; } -bool LLPluginMessagePipe::pumpOutput() +static apr_interval_time_t const flush_max_block_time = 10000000; // Even when flushing, give up after 10 seconds. +static apr_interval_time_t const flush_min_timeout = 1000; // When flushing, initially timeout after 1 ms. +static apr_interval_time_t const flush_max_timeout = 50000; // Never wait longer than 50 ms. + +// DO NOT SET 'flush' TO TRUE WHEN CALLED ON THE VIEWER SIDE! +// flush is only intended for plugin-side. +bool LLPluginMessagePipe::pumpOutput(bool flush) { bool result = true; if(mSocket) { - apr_status_t status; - apr_size_t size; + apr_interval_time_t flush_time_left_usec = flush_max_block_time; + apr_interval_time_t timeout_usec = flush ? flush_min_timeout : 0; LLMutexLock lock(&mOutputMutex); - if(!mOutput.empty()) + while(result && !mOutput.empty()) { // write any outgoing messages - size = (apr_size_t)mOutput.size(); + apr_size_t size = (apr_size_t)mOutput.size(); - setSocketTimeout(0); + setSocketTimeout(timeout_usec); // LL_INFOS("Plugin") << "before apr_socket_send, size = " << size << LL_ENDL; - status = apr_socket_send( + apr_status_t status = apr_socket_send( mSocket->getSocket(), (const char*)mOutput.data(), &size); @@ -193,12 +209,29 @@ bool LLPluginMessagePipe::pumpOutput() { // success mOutput = mOutput.substr(size); + break; } - else if(APR_STATUS_IS_EAGAIN(status)) + else if(APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { // Socket buffer is full... // remove the written part from the buffer and try again later. mOutput = mOutput.substr(size); + if (!flush) + break; + flush_time_left_usec -= timeout_usec; + if (flush_time_left_usec <= 0) + { + result = false; + } + else if (size == 0) + { + // Nothing at all was written. Increment wait time. + timeout_usec = std::min(flush_max_timeout, 2 * timeout_usec); + } + else + { + timeout_usec = std::max(flush_min_timeout, timeout_usec / 2); + } } else if(APR_STATUS_IS_EOF(status)) { diff --git a/indra/llplugin/llpluginmessagepipe.h b/indra/llplugin/llpluginmessagepipe.h index 6eedca27f..1e71caf42 100644 --- a/indra/llplugin/llpluginmessagepipe.h +++ b/indra/llplugin/llpluginmessagepipe.h @@ -61,6 +61,8 @@ protected: bool canSendMessage(void); // call this to send a message over the pipe bool writeMessageRaw(const std::string &message); + // call this to attempt to flush all messages for 10 seconds long. + bool flushMessages(void); // call this to close the pipe void killMessagePipe(void); @@ -79,8 +81,10 @@ public: void clearOwner(void); bool pump(F64 timeout = 0.0f); - bool pumpOutput(); + bool pumpOutput(bool flush = false); bool pumpInput(F64 timeout = 0.0f); + + bool flushMessages(void) { return pumpOutput(true); } protected: void processInput(void); diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index 3ba765bf6..fdcb4f6a8 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -556,11 +556,22 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message) } else if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) { + bool flush = false; std::string message_name = parsed.getName(); if(message_name == "shutdown") { // The plugin is finished. setState(STATE_UNLOADING); + flush = true; + } + else if (message_name == "flush") + { + flush = true; + passMessage = false; + } + if (flush) + { + flushMessages(); } } } diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index fc94ee849..0d361324e 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -104,6 +104,7 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner) mDebug = false; mBlocked = false; mPolledInput = false; + mReceivedShutdown = false; mPollFD.client_data = NULL; mPollFDPool.create(); @@ -165,6 +166,8 @@ void LLPluginProcessParent::errorState(void) { if(mState < STATE_RUNNING) setState(STATE_LAUNCH_FAILURE); + else if (mReceivedShutdown) + setState(STATE_EXITING); else setState(STATE_ERROR); } @@ -1012,6 +1015,7 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) else if(message_name == "shutdown") { LL_INFOS("Plugin") << "received shutdown message" << LL_ENDL; + mReceivedShutdown = true; mOwner->receivedShutdown(); } else if(message_name == "shm_add_response") diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h index 1ec5ef4c3..65455950f 100644 --- a/indra/llplugin/llpluginprocessparent.h +++ b/indra/llplugin/llpluginprocessparent.h @@ -145,8 +145,8 @@ private: STATE_RUNNING, // STATE_LAUNCH_FAILURE, // Failure before plugin loaded STATE_ERROR, // generic bailout state - STATE_CLEANUP, // clean everything up STATE_EXITING, // Tried to kill process, waiting for it to exit + STATE_CLEANUP, // clean everything up STATE_DONE // }; @@ -183,6 +183,7 @@ private: bool mDebug; bool mBlocked; bool mPolledInput; + bool mReceivedShutdown; LLProcessLauncher mDebugger; diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index e3a09009d..f8da4da86 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -64,31 +64,41 @@ AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mCanceled(false) { } +// static +AITHREADSAFESIMPLE(AIFilePicker::context_map_type, AIFilePicker::sContextMap, ); + +// static void AIFilePicker::store_folder(std::string const& context, std::string const& folder) { if (!folder.empty()) { - mContextMap[context] = folder; + LL_DEBUGS("Plugin") << "AIFilePicker::mContextMap[\"" << context << "\"] = \"" << folder << '"' << LL_ENDL; + AIAccess(sContextMap)->operator[](context) = folder; } } +// static std::string AIFilePicker::get_folder(std::string const& default_path, std::string const& context) { - context_map_type::iterator iter = mContextMap.find(context); - if (iter != mContextMap.end()) + AIAccess wContextMap(sContextMap); + context_map_type::iterator iter = wContextMap->find(context); + if (iter != wContextMap->end()) { return iter->second; } else if (!default_path.empty()) { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found. Returning default_path \"" << default_path << "\"." << LL_ENDL; return default_path; } - else if ((iter = mContextMap.find("savefile")) != mContextMap.end()) + else if ((iter = wContextMap->find("savefile")) != wContextMap->end()) { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found and default_path empty. Returning context \"savefile\": \"" << iter->second << "\"." << LL_ENDL; return iter->second; } else { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found, default_path empty and context \"savefile\" not found. Returning \"$HOME\"." << LL_ENDL; // This is the last resort when all else failed. Open the file chooser in directory 'home'. char const* home = NULL; #if LL_WINDOWS @@ -103,7 +113,7 @@ std::string AIFilePicker::get_folder(std::string const& default_path, std::strin void AIFilePicker::open(ELoadFilter filter, std::string const& default_path, std::string const& context, bool multiple) { mContext = context; - mFolder = get_folder(default_path, context); + mFolder = AIFilePicker::get_folder(default_path, context); mOpenType = multiple ? load_multiple : load; switch(filter) { @@ -152,7 +162,7 @@ void AIFilePicker::open(std::string const& filename, ESaveFilter filter, std::st { mFilename = filename; mContext = context; - mFolder = get_folder(default_path, context); + mFolder = AIFilePicker::get_folder(default_path, context); mOpenType = save; switch(filter) { @@ -372,7 +382,7 @@ void AIFilePicker::multiplex_impl(void) case AIFilePicker_done: { // Store folder of first filename as context. - store_folder(mContext, getFolder()); + AIFilePicker::store_folder(mContext, getFolder()); finish(); } } diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index a1709eeb8..91a7bc819 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -176,9 +176,8 @@ public: private: LLPointer mPluginManager; //!< Pointer to the plugin manager. - // AIFIXME: this should be a separate, thread-safe singleton. typedef std::map context_map_type; //!< Type of mContextMap. - context_map_type mContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. + static AIThreadSafeSimple sContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. std::string mContext; //!< Some key to indicate the context (remembers the folder per key). // Input variables (cache variable between call to open and run). @@ -191,9 +190,9 @@ private: std::vector mFilenames; //!< Filesnames. // Store a folder for the given context. - void store_folder(std::string const& context, std::string const& folder); + static void store_folder(std::string const& context, std::string const& folder); // Return the last folder stored for 'context', or default_path if none, or context "savefile" if empty, or $HOME if none. - std::string get_folder(std::string const& default_path, std::string const& context); + static std::string get_folder(std::string const& default_path, std::string const& context); protected: // Call finish() (or abort()), not delete. diff --git a/indra/plugins/base_basic/basic_plugin_base.cpp b/indra/plugins/base_basic/basic_plugin_base.cpp index f791b5887..d1a656391 100755 --- a/indra/plugins/base_basic/basic_plugin_base.cpp +++ b/indra/plugins/base_basic/basic_plugin_base.cpp @@ -115,6 +115,20 @@ void BasicPluginBase::sendLogMessage(std::string const& message, LLPluginMessage logmessage.setValue("message", message); logmessage.setValueS32("log_level",level); mSendMessageFunction(logmessage.generate().c_str(), &mPluginInstance); + // Theoretically we should flush here (that's what PLS_ENDL means), but we don't because + // that interfers with how the code would act without debug messages. Instead, the developer + // is responsible to call flushMessages themselves at the appropriate places. +} + +/** + * Flush all messages to the viewer. + * + * This blocks if necessary, up till 10 seconds, before it gives up. + */ +void BasicPluginBase::flushMessages(void) +{ + LLPluginMessage logmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "flush"); + mSendMessageFunction(logmessage.generate().c_str(), &mPluginInstance); } /** @@ -124,6 +138,9 @@ void BasicPluginBase::sendLogMessage(std::string const& message, LLPluginMessage */ void BasicPluginBase::sendShutdownMessage(void) { + // Do a double flush. First flush all messages so far, then send the shutdown message, + // which also will try to flush itself before terminating the process. + flushMessages(); LLPluginMessage shutdownmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shutdown"); sendMessage(shutdownmessage); } diff --git a/indra/plugins/base_basic/basic_plugin_base.h b/indra/plugins/base_basic/basic_plugin_base.h index 492b408e7..042a27b63 100755 --- a/indra/plugins/base_basic/basic_plugin_base.h +++ b/indra/plugins/base_basic/basic_plugin_base.h @@ -36,16 +36,13 @@ #ifndef BASIC_PLUGIN_BASE_H #define BASIC_PLUGIN_BASE_H -#include - -#include - #include "linden_common.h" - #include "llplugininstance.h" #include "llpluginmessage.h" #include "llpluginmessageclasses.h" +#include + class BasicPluginBase { public: @@ -65,7 +62,10 @@ public: // Used for log messages. Use macros below. void sendLogMessage(std::string const& message, LLPluginMessage::LLPLUGIN_LOG_LEVEL level); - // Shoot down the whole process. + // Flush all messages to the viewer. + void flushMessages(void); + + // Flush all messages and then shoot down the whole process. void sendShutdownMessage(void); protected: @@ -127,6 +127,7 @@ int create_plugin( #define PLS_INFOS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_INFO, LL_DEBUG_PLUGIN_MESSAGES) #define PLS_WARNS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_WARN, 1) #define PLS_ERRS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_ERR, 1) +#define PLS_FLUSH do { BasicPluginBase::sPluginBase->flushMessages(); } while(0) #endif // BASIC_PLUGIN_BASE diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp index 1201c3f71..7e3a4f0b0 100644 --- a/indra/plugins/filepicker/basic_plugin_filepicker.cpp +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -232,10 +232,13 @@ void FilepickerPlugin::receiveMessage(char const* message_string) std::string folder = message_in.getValue("folder"); bool get_directory = (filter == "directory"); + // We about to completely block on running the modal File/Dir picker window, so flush any pending messages to the viewer. + flushMessages(); + bool canceled; if (get_directory) { - canceled = !LLDirPicker::instance().getDir(&folder); + canceled = !LLDirPicker::instance().getDir(folder); } else if (type == "save") { @@ -275,6 +278,7 @@ void FilepickerPlugin::receiveMessage(char const* message_string) sendMessage(message); } // We're done. Exit the whole application. + // This first flushes any messages before terminating the plugin. sendShutdownMessage(); } else diff --git a/indra/plugins/filepicker/legacy.cpp b/indra/plugins/filepicker/legacy.cpp index f89862061..e106923bf 100644 --- a/indra/plugins/filepicker/legacy.cpp +++ b/indra/plugins/filepicker/legacy.cpp @@ -28,8 +28,8 @@ * - Initial version, written by Aleric Inglewood @ SL */ -#include "legacy.h" #include "basic_plugin_base.h" // For PLS_INFOS etc. +#include "legacy.h" // Translation map. translation_map_type translation_map; diff --git a/indra/plugins/filepicker/lldirpicker.cpp b/indra/plugins/filepicker/lldirpicker.cpp index 39dff05e9..7ddcda6df 100644 --- a/indra/plugins/filepicker/lldirpicker.cpp +++ b/indra/plugins/filepicker/lldirpicker.cpp @@ -65,7 +65,15 @@ LLDirPicker::~LLDirPicker() // nothing } -BOOL LLDirPicker::getDir(std::string* filename) +static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED) + { + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData); + } +} + +BOOL LLDirPicker::getDir(std::string const& folder) { if( mLocked ) { @@ -73,34 +81,38 @@ BOOL LLDirPicker::getDir(std::string* filename) } BOOL success = FALSE; - BROWSEINFO bi; - memset(&bi, 0, sizeof(bi)); + BROWSEINFO bi; + memset(&bi, 0, sizeof(bi)); - bi.ulFlags = BIF_USENEWUI; - bi.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); - bi.lpszTitle = NULL; + bi.ulFlags = BIF_USENEWUI; + bi.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); + bi.lpszTitle = NULL; + llutf16string tstring = utf8str_to_utf16str(folder); + bi.lParam = (LPARAM)tstring.c_str(); + bi.lpfn = &BrowseCallbackProc; - ::OleInitialize(NULL); + ::OleInitialize(NULL); - LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi); + PLS_FLUSH; + LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi); - if(pIDL != NULL) - { - WCHAR buffer[_MAX_PATH] = {'\0'}; + if(pIDL != NULL) + { + WCHAR buffer[_MAX_PATH] = {'\0'}; - if(::SHGetPathFromIDList(pIDL, buffer) != 0) - { - // Set the string value. + if(::SHGetPathFromIDList(pIDL, buffer) != 0) + { + // Set the string value. - mDir = utf16str_to_utf8str(llutf16string(buffer)); - success = TRUE; - } + mDir = utf16str_to_utf8str(llutf16string(buffer)); + success = TRUE; + } - // free the item id list - CoTaskMemFree(pIDL); - } + // free the item id list + CoTaskMemFree(pIDL); + } - ::OleUninitialize(); + ::OleUninitialize(); return success; } @@ -147,9 +159,9 @@ pascal void LLDirPicker::doNavCallbackEvent(NavEventCallbackMessage callBackSele //Convert string to a FSSpec FSRef myFSRef; - const char* filename=sInstance.mFileName->c_str(); + const char* folder = sInstance.mFileName->c_str(); - error = FSPathMakeRef ((UInt8*)filename, &myFSRef, NULL); + error = FSPathMakeRef ((UInt8*)folder, &myFSRef, NULL); if (error != noErr) break; @@ -185,7 +197,10 @@ OSStatus LLDirPicker::doNavChooseDialog() gViewerWindow->mWindow->beforeDialog(); if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } gViewerWindow->mWindow->afterDialog(); @@ -216,13 +231,13 @@ OSStatus LLDirPicker::doNavChooseDialog() return error; } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string const& folder) { if( mLocked ) return FALSE; BOOL success = FALSE; OSStatus error = noErr; - mFileName = filename; + mFileName = folder; // mNavOptions.saveFileName @@ -266,14 +281,15 @@ void LLDirPicker::reset() LLFilePickerBase::reset(); } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string const& folder) { reset(); - GtkWindow* picker = buildFilePicker(false, true, "dirpicker"); + GtkWindow* picker = buildFilePicker(false, true, folder); if (picker) { gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("choose_the_directory").c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); return (!getFirstFile().empty()); } @@ -301,7 +317,7 @@ void LLDirPicker::reset() { } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string* folder) { return FALSE; } diff --git a/indra/plugins/filepicker/lldirpicker.h b/indra/plugins/filepicker/lldirpicker.h index cdee1d094..034bcdf91 100644 --- a/indra/plugins/filepicker/lldirpicker.h +++ b/indra/plugins/filepicker/lldirpicker.h @@ -71,7 +71,7 @@ public: // calling this before main() is undefined static LLDirPicker& instance( void ) { return sInstance; } - BOOL getDir(std::string* filename); + BOOL getDir(std::string const& filename); std::string getDirName(); // clear any lists of buffers or whatever, and make sure the dir diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index 48069e88c..066a8340c 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -205,7 +205,6 @@ bool LLFilePickerBase::setupFilter(ELoadFilter filter) return res; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -214,9 +213,9 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder } bool success = FALSE; - // don't provide default file selection + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mFilesW[0] = '\0'; - mOFN.lpstrFile = mFilesW; mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ; @@ -237,7 +236,6 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -246,9 +244,9 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons } bool success = FALSE; - // don't provide default file selection + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mFilesW[0] = '\0'; - mOFN.lpstrFile = mFilesW; mOFN.nFilterIndex = 1; mOFN.nMaxFile = FILENAME_BUFFER_SIZE; @@ -295,7 +293,6 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -304,11 +301,13 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena } bool success = FALSE; + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mOFN.lpstrFile = mFilesW; if (!filename.empty()) { llutf16string tstring = utf8str_to_utf16str(filename); - wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ + wcsncpy(mFilesW, tstring.data(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ else { mFilesW[0] = '\0'; @@ -705,8 +704,9 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) { + LLFilePickerBase* picker = (LLFilePickerBase*)callBackUD; Boolean result = true; - ELoadFilter filter = *((ELoadFilter*) callBackUD); + ELoadFilter filter = picker->getLoadFilter(); OSStatus error = noErr; if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS)) @@ -788,7 +788,7 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c } else if (filter == FFLOAD_RAW) { - if (fileInfo.filetype != '\?\?\?\?' && + if (fileInfo.filetype != L'\?\?\?\?' && (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) ) { @@ -809,23 +809,70 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c return result; } -OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) +//static +pascal void LLFilePickerBase::doNavCallbackEvent(NavEventCallbackMessage callBackSelector, + NavCBRecPtr callBackParms, void* callBackUD) +{ + switch(callBackSelector) + { + case kNavCBStart: + { + LLFilePickerBase* picker = reinterpret_cast(callBackUD); + if (picker->getFolder().empty()) break; + + OSStatus error = noErr; + AEDesc theLocation = {typeNull, NULL}; + FSSpec outFSSpec; + + //Convert string to a FSSpec + FSRef myFSRef; + + const char* folder = picker->getFolder().c_str(); + + error = FSPathMakeRef ((UInt8*)folder, &myFSRef, NULL); + + if (error != noErr) break; + + error = FSGetCatalogInfo (&myFSRef, kFSCatInfoNone, NULL, NULL, &outFSSpec, NULL); + + if (error != noErr) break; + + error = AECreateDesc(typeFSS, &outFSSpec, sizeof(FSSpec), &theLocation); + + if (error != noErr) break; + + error = NavCustomControl(callBackParms->context, + kNavCtlSetLocation, (void*)&theLocation); + + } + } +} + +OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter, std::string const& folder) { OSStatus error = noErr; NavDialogRef navRef = NULL; NavReplyRecord navReply; memset(&navReply, 0, sizeof(navReply)); + + mLoadFilter = filter; + mFolder = folder; + + NavEventUPP eventProc = NewNavEventUPP(doNavCallbackEvent); // NOTE: we are passing the address of a local variable here. // This is fine, because the object this call creates will exist for less than the lifetime of this function. // (It is destroyed by NavDialogDispose() below.) - error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef); + error = NavCreateChooseFileDialog(&mNavOptions, NULL, eventProc, NULL, navOpenFilterProc, (void*)this, &navRef); //gViewerWindow->mWindow->beforeDialog(); if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } //gViewerWindow->mWindow->afterDialog(); @@ -860,11 +907,14 @@ OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) mFiles.push_back(std::string(path)); } } + + if (eventProc) + DisposeNavEventUPP(eventProc); return error; } -OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string& filename) +OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, std::string const& filename, std::string const& folder) { OSStatus error = noErr; NavDialogRef navRef = NULL; @@ -905,46 +955,49 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string extension = CFSTR(".png"); break; case FFSAVE_AVI: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".mov"); break; case FFSAVE_ANIM: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".xaf"); break; #ifdef _CORY_TESTING case FFSAVE_GEOMETRY: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".slg"); break; #endif case FFSAVE_RAW: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".raw"); break; case FFSAVE_J2C: - type = '\?\?\?\?'; + type = L'\?\?\?\?'; creator = 'prvw'; extension = CFSTR(".j2c"); break; case FFSAVE_ALL: default: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(""); break; } + + NavEventUPP eventUPP = NewNavEventUPP(navSetDefaultFolderProc); + mFolder = folder; // Create the dialog - error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef); + error = NavCreatePutFileDialog(&mNavOptions, type, creator, eventUPP, (void*)this, &navRef); if (error == noErr) { CFStringRef nameString = NULL; @@ -981,7 +1034,10 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string // Run the dialog if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } //gViewerWindow->mWindow->afterDialog(); @@ -1033,10 +1089,12 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string } } + if (eventUPP) + DisposeNavEventUPP(eventUPP); + return error; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1050,7 +1108,7 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; { - error = doNavChooseDialog(filter); + error = doNavChooseDialog(filter, folder); } if (error == noErr) { @@ -1061,7 +1119,6 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1075,7 +1132,7 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons mNavOptions.optionFlags |= kNavAllowMultipleFiles; { - error = doNavChooseDialog(filter); + error = doNavChooseDialog(filter, folder); } if (error == noErr) { @@ -1088,7 +1145,6 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -1101,7 +1157,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; { - error = doNavSaveDialog(filter, filename); + error = doNavSaveDialog(filter, filename, folder); } if (error == noErr) { @@ -1203,6 +1259,7 @@ GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std:: if (!folder.empty()) { + PLS_DEBUGS << "Calling gtk_file_chooser_set_current_folder(\"" << folder << "\")." << PLS_ENDL; gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(win), folder.c_str()); @@ -1225,6 +1282,7 @@ GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std:: G_CALLBACK(LLFilePickerBase::chooser_responder), this); + PLS_FLUSH; gtk_window_set_modal(GTK_WINDOW(win), TRUE); /* GTK 2.6: if (is_folder) @@ -1383,6 +1441,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena } gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = (getFileCount() == 1); @@ -1430,6 +1489,7 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = (getFileCount() == 1); @@ -1458,6 +1518,7 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = !mFiles.empty(); } @@ -1485,7 +1546,6 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena return FALSE; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { reset(); diff --git a/indra/plugins/filepicker/llfilepicker.h b/indra/plugins/filepicker/llfilepicker.h index f8e17719e..9c8db8e8a 100644 --- a/indra/plugins/filepicker/llfilepicker.h +++ b/indra/plugins/filepicker/llfilepicker.h @@ -166,6 +166,9 @@ private: OSStatus doNavChooseDialog(ELoadFilter filter); OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename); static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); + static pascal void LLFilePickerBase::doNavCallbackEvent(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void* callBackUD); + ELoadFilter getLoadFilter(void) const { return mLoadFilter; } + std::string const& getFolder(void) const { return mFolder; } #endif // LL_DARWIN #if LL_GTK @@ -194,7 +197,11 @@ private: S32 mCurrentFile; bool mLocked; bool mMultiFile; - +#if LL_DARWIN + ELoadFilter mLoadFilter; + std::string mFolder; +#endif + protected: #if LL_GTK GtkWindow* buildFilePicker(bool is_save, bool is_folder, std::string const& folder);