saves models to settings to avoid reload, with option to force from menu
This commit is contained in:
parent
dd033a7896
commit
177cdb925f
10 changed files with 269 additions and 42 deletions
BIN
.genio
BIN
.genio
Binary file not shown.
67
App.cpp
67
App.cpp
|
@ -9,12 +9,25 @@
|
||||||
#include <AboutWindow.h>
|
#include <AboutWindow.h>
|
||||||
#include <Catalog.h>
|
#include <Catalog.h>
|
||||||
|
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
#define B_TRANSLATION_CONTEXT "Application"
|
#define B_TRANSLATION_CONTEXT "Application"
|
||||||
|
|
||||||
const char *kApplicationSignature = "application/x-vnd.SLema-DumBer";
|
const char *kApplicationSignature = "application/x-vnd.SLema-DumBer";
|
||||||
|
|
||||||
App::App() : BApplication(kApplicationSignature) {
|
App::App() : BApplication(kApplicationSignature) {
|
||||||
|
|
||||||
|
BMessage s = LoadMessageFromFile(kSettingsFileName);
|
||||||
|
|
||||||
|
if (s.what=='STNG')
|
||||||
|
settingsMessage = s;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
settingsMessage.what = 'STNG';
|
||||||
|
settingsMessage.AddInt32("settings_format_version", 1); // Initial data
|
||||||
|
}
|
||||||
|
|
||||||
MainWindow *m = new MainWindow();
|
MainWindow *m = new MainWindow();
|
||||||
m->SetLook(B_DOCUMENT_WINDOW_LOOK);
|
m->SetLook(B_DOCUMENT_WINDOW_LOOK);
|
||||||
m->SetFeel(B_NORMAL_WINDOW_FEEL);
|
m->SetFeel(B_NORMAL_WINDOW_FEEL);
|
||||||
|
@ -44,8 +57,62 @@ void App::AboutRequested() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void App::MessageReceived(BMessage *message) {
|
||||||
|
|
||||||
|
switch (message->what) {
|
||||||
|
|
||||||
|
case kSettingsUpdate: {
|
||||||
|
|
||||||
|
// NO DO NOT CLEAR message, we can just overwrite one field and save the whole thing each time
|
||||||
|
//settingsMessage.MakeEmpty();
|
||||||
|
|
||||||
|
std::cout << "Received settings update: " ;
|
||||||
|
|
||||||
|
|
||||||
|
int32 count = message->CountNames(B_ANY_TYPE);
|
||||||
|
for (int32 index = 0; index < count; ++index) {
|
||||||
|
char* name;
|
||||||
|
uint32 type;
|
||||||
|
int32 itemCount;
|
||||||
|
|
||||||
|
std::cout << "Received settings update - key: " << name;
|
||||||
|
|
||||||
|
// Retrieve information about the item at the index
|
||||||
|
if (message->GetInfo(B_ANY_TYPE, index, &name, &type, &itemCount) == B_NO_ERROR) {
|
||||||
|
|
||||||
|
if (type == B_STRING_TYPE) {
|
||||||
|
BString value;
|
||||||
|
if (message->FindString(name, &value) == B_NO_ERROR) {
|
||||||
|
settingsMessage.AddString(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SaveMessageToFile(settingsMessage,kSettingsFileName);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
|
||||||
|
default: {
|
||||||
|
// message->PrintToStream();
|
||||||
|
BHandler::MessageReceived(
|
||||||
|
message); // call the parent handler for other messages
|
||||||
|
// _infoView->SetText(message->FindMessage());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end switch
|
||||||
|
|
||||||
|
} // end function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
App *app = new App();
|
App *app = new App();
|
||||||
|
|
||||||
|
|
||||||
app->Run();
|
app->Run();
|
||||||
delete app;
|
delete app;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
4
App.h
4
App.h
|
@ -16,10 +16,14 @@ public:
|
||||||
App();
|
App();
|
||||||
virtual ~App();
|
virtual ~App();
|
||||||
|
|
||||||
|
|
||||||
virtual void AboutRequested();
|
virtual void AboutRequested();
|
||||||
|
|
||||||
MainWindow* mainWindow = nullptr;
|
MainWindow* mainWindow = nullptr;
|
||||||
|
|
||||||
|
BMessage settingsMessage;
|
||||||
|
BMessage* getSettingsMessage() { return &settingsMessage; }
|
||||||
|
virtual void MessageReceived(BMessage *msg);
|
||||||
|
|
||||||
void ReadyToRun()
|
void ReadyToRun()
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
|
|
||||||
#include <Application.h>
|
#include <Application.h>
|
||||||
|
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "App.h"
|
||||||
|
|
||||||
|
|
||||||
Conversation::Conversation(BHandler *replyTo) {
|
Conversation::Conversation(BHandler *replyTo) {
|
||||||
|
|
||||||
replyTarget = replyTo;
|
replyTarget = replyTo;
|
||||||
|
@ -49,8 +53,7 @@ void Conversation::ClearHistory() {
|
||||||
_messageHistory.clear();
|
_messageHistory.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string>
|
status_t Conversation::FilterTextModels(const json &modelsJson) {
|
||||||
Conversation::FilterTextModels(const json &modelsJson) {
|
|
||||||
std::vector<std::string> result;
|
std::vector<std::string> result;
|
||||||
std::regex pattern("gpt|text|curie|babbage|ada");
|
std::regex pattern("gpt|text|curie|babbage|ada");
|
||||||
|
|
||||||
|
@ -70,9 +73,26 @@ Conversation::FilterTextModels(const json &modelsJson) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.empty())
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
|
||||||
std::sort(result.begin(), result.end(),
|
std::sort(result.begin(), result.end(),
|
||||||
std::greater<>()); // inverse alphabetical to get gpt-4 on top
|
std::greater<>()); // inverse alphabetical to get gpt-4 on top
|
||||||
return result;
|
|
||||||
|
PrintAsJsonArray(result);
|
||||||
|
|
||||||
|
BMessage msg(kModelsReceived);
|
||||||
|
for (const auto &model : result) {
|
||||||
|
msg.AddString("model", model.c_str());
|
||||||
|
}
|
||||||
|
sendReply(msg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::MessageReceived(BMessage *message) {
|
void Conversation::MessageReceived(BMessage *message) {
|
||||||
|
@ -148,18 +168,13 @@ void Conversation::MessageReceived(BMessage *message) {
|
||||||
|
|
||||||
if (objType == "list") {
|
if (objType == "list") {
|
||||||
// printf("full Reply as text:%s",body.text.value().String());
|
// printf("full Reply as text:%s",body.text.value().String());
|
||||||
|
if (FilterTextModels(parsed)==B_OK)
|
||||||
std::vector validModels = FilterTextModels(parsed);
|
{
|
||||||
PrintAsJsonArray(validModels);
|
//Save models in settings so we don't have to do one request at start each time
|
||||||
BMessage msg(kModelsReceived);
|
BMessage message(kSettingsUpdate);
|
||||||
|
message.AddString("models_json", BString(fullBody));
|
||||||
for (const auto &model : validModels) {
|
be_app->PostMessage(&message);
|
||||||
msg.AddString("model", model.c_str());
|
|
||||||
}
|
}
|
||||||
sendReply(msg);
|
|
||||||
|
|
||||||
// std::string content =
|
|
||||||
// parsed["choices"][0]["message"]["content"];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,20 +251,20 @@ void Conversation::MessageReceived(BMessage *message) {
|
||||||
sendReply(msgr);
|
sendReply(msgr);
|
||||||
|
|
||||||
|
|
||||||
BMessage msg(kShowError);
|
BMessage msg(kShowStatus);
|
||||||
msg.AddString("text", e.DebugMessage());
|
msg.AddString("text", e.DebugMessage());
|
||||||
sendReply(msg);
|
sendReply(msg);
|
||||||
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cout << "Caught a standard exception: " << e.what() << std::endl;
|
std::cout << "Caught a standard exception: " << e.what() << std::endl;
|
||||||
BMessage msg(kShowError);
|
BMessage msg(kShowStatus);
|
||||||
msg.AddString("text", e.what());
|
msg.AddString("text", e.what());
|
||||||
sendReply(msg);
|
sendReply(msg);
|
||||||
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::cout << "Caught an unknown exception!" << std::endl;
|
std::cout << "Caught an unknown exception!" << std::endl;
|
||||||
BMessage msg(kShowError);
|
BMessage msg(kShowStatus);
|
||||||
msg.AddString("text", "unknown exception");
|
msg.AddString("text", "unknown exception");
|
||||||
sendReply(msg);
|
sendReply(msg);
|
||||||
}
|
}
|
||||||
|
@ -268,7 +283,40 @@ std::string Conversation::buildBearerKey() {
|
||||||
return bearer;
|
return bearer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::loadModels() {
|
void Conversation::loadModelsForced(bool force) {
|
||||||
|
|
||||||
|
// TRY first to get models from settings files
|
||||||
|
if (force==false) {
|
||||||
|
App* app = dynamic_cast<App*>(be_app);
|
||||||
|
if (app) {
|
||||||
|
BMessage* settings = app->getSettingsMessage();
|
||||||
|
const char* jsonString;
|
||||||
|
|
||||||
|
if (settings->FindString("models_json", &jsonString)==B_NO_ERROR)
|
||||||
|
{
|
||||||
|
json parsed = json::parse(jsonString);
|
||||||
|
std::string objType = parsed["object"];
|
||||||
|
if (objType == "list") {
|
||||||
|
//printf("loadModelFromSettings :%s",jsonString);
|
||||||
|
|
||||||
|
if(FilterTextModels(parsed)==B_OK)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
std::cout << "no models in settings object...\n";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MODELS not in settings, ask them.
|
||||||
|
|
||||||
|
|
||||||
|
BMessage msg(kShowStatus);
|
||||||
|
msg.AddString("text", "Asking server for list of models...");
|
||||||
|
sendReply(msg);
|
||||||
|
|
||||||
|
|
||||||
auto url = BUrl("https://api.openai.com/v1/models", true);
|
auto url = BUrl("https://api.openai.com/v1/models", true);
|
||||||
BHttpRequest request = BHttpRequest(url);
|
BHttpRequest request = BHttpRequest(url);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
static const uint32 kShowError = 'serr';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,11 +63,11 @@ public:
|
||||||
virtual void MessageReceived(BMessage *msg);
|
virtual void MessageReceived(BMessage *msg);
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string> FilterTextModels(const json& modelsJson);
|
status_t FilterTextModels(const json& modelsJson);
|
||||||
|
|
||||||
void ask(const std::string& prompt);
|
void ask(const std::string& prompt);
|
||||||
void setModel(const std::string& prompt);
|
void setModel(const std::string& prompt);
|
||||||
void loadModels();
|
void loadModelsForced(bool force);
|
||||||
void PrintAsJsonArray(const std::vector<std::string>& models) ;
|
void PrintAsJsonArray(const std::vector<std::string>& models) ;
|
||||||
void ClearHistory();
|
void ClearHistory();
|
||||||
std::string buildHistoryInfoLine();
|
std::string buildHistoryInfoLine();
|
||||||
|
|
|
@ -28,9 +28,10 @@ static bool progressColorUp = false;
|
||||||
#include <View.h>
|
#include <View.h>
|
||||||
|
|
||||||
#include <Path.h>
|
#include <Path.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
|
||||||
#include "Conversation.h"
|
#include "Conversation.h"
|
||||||
#include <FindDirectory.h>
|
#include "Utils.h"
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
#define B_TRANSLATION_CONTEXT "Window"
|
#define B_TRANSLATION_CONTEXT "Window"
|
||||||
|
@ -236,11 +237,12 @@ void MainWindow::checkValidKey() {
|
||||||
progressColor = 0;
|
progressColor = 0;
|
||||||
progressAnim = 1;
|
progressAnim = 1;
|
||||||
|
|
||||||
_conversation->loadModels();
|
|
||||||
_infoView->SetText("Requesting model lists...");
|
_conversation->loadModelsForced(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::ShowMissingKeyAlertAndQuit() {
|
void MainWindow::ShowMissingKeyAlertAndQuit() {
|
||||||
|
|
||||||
BAlert *alert = new BAlert(
|
BAlert *alert = new BAlert(
|
||||||
|
@ -303,14 +305,26 @@ void MainWindow::MessageReceived(BMessage *message) {
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case kShowError: {
|
case kRequestModels: {
|
||||||
|
|
||||||
printf("error received:");
|
printf("will Request models");
|
||||||
|
//_infoView->SetText("Cleared conversation history. Starting new context");
|
||||||
|
//_inputField->SetText("");
|
||||||
|
//_answerView->SetText("");
|
||||||
|
//_conversation->ClearHistory();
|
||||||
|
_conversation->loadModelsForced(true);
|
||||||
|
//updateHistoryInfo();
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case kShowStatus: {
|
||||||
|
|
||||||
|
printf("status or error received:");
|
||||||
const char *text;
|
const char *text;
|
||||||
|
|
||||||
if (message->FindString("text", &text) == B_OK) {
|
if (message->FindString("text", &text) == B_OK) {
|
||||||
_infoView->SetText(BString("ERROR: ") << text);
|
_infoView->SetText(BString("Status: ") << text);
|
||||||
printf("ERROR: %s\n", text);
|
printf("Status: %s\n", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
@ -517,6 +531,8 @@ BMenuBar *MainWindow::_BuildMenu() {
|
||||||
|
|
||||||
menu->AddSeparatorItem();
|
menu->AddSeparatorItem();
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
item = new BMenuItem(B_TRANSLATE("Clear History" B_UTF8_ELLIPSIS),
|
item = new BMenuItem(B_TRANSLATE("Clear History" B_UTF8_ELLIPSIS),
|
||||||
new BMessage(kClearHistory));
|
new BMessage(kClearHistory));
|
||||||
item->SetTarget(this);
|
item->SetTarget(this);
|
||||||
|
@ -525,12 +541,22 @@ BMenuBar *MainWindow::_BuildMenu() {
|
||||||
|
|
||||||
menuBar->AddItem(menu);
|
menuBar->AddItem(menu);
|
||||||
|
|
||||||
|
|
||||||
menu->AddSeparatorItem();
|
menu->AddSeparatorItem();
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
item = new BMenuItem(B_TRANSLATE("View full json" B_UTF8_ELLIPSIS),
|
item = new BMenuItem(B_TRANSLATE("View full json" B_UTF8_ELLIPSIS),
|
||||||
new BMessage(kViewJSON));
|
new BMessage(kViewJSON));
|
||||||
item->SetTarget(this);
|
item->SetTarget(this);
|
||||||
item->SetShortcut('J', B_COMMAND_KEY | B_SHIFT_KEY);
|
item->SetShortcut('J', B_COMMAND_KEY | B_SHIFT_KEY);
|
||||||
menu->AddItem(item);
|
menu->AddItem(item);
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
item = new BMenuItem(B_TRANSLATE("Update models..." B_UTF8_ELLIPSIS),
|
||||||
|
new BMessage(kRequestModels));
|
||||||
|
item->SetTarget(this);
|
||||||
|
//item->SetShortcut('J', B_COMMAND_KEY | B_SHIFT_KEY);
|
||||||
|
menu->AddItem(item);
|
||||||
|
|
||||||
return menuBar;
|
return menuBar;
|
||||||
}
|
}
|
||||||
|
|
13
MainWindow.h
13
MainWindow.h
|
@ -17,18 +17,6 @@
|
||||||
|
|
||||||
#include "Conversation.h"
|
#include "Conversation.h"
|
||||||
|
|
||||||
static const uint32 kCheckKey = 'chkk';
|
|
||||||
static const uint32 kMsgNewFile = 'fnew';
|
|
||||||
static const uint32 kMsgOpenFile = 'fopn';
|
|
||||||
static const uint32 kMsgSaveFile = 'fsav';
|
|
||||||
static const uint32 kModelSelected = 'msel';
|
|
||||||
static const uint32 kViewJSON = 'vjso';
|
|
||||||
|
|
||||||
static const uint32 kPulse = 'plse';
|
|
||||||
|
|
||||||
static const uint32 kSendPrompt = 'kspt';
|
|
||||||
static const uint32 kQuestionChanged = 'kqch';
|
|
||||||
|
|
||||||
class MainWindow : public BWindow {
|
class MainWindow : public BWindow {
|
||||||
public:
|
public:
|
||||||
MainWindow();
|
MainWindow();
|
||||||
|
@ -71,6 +59,7 @@ private:
|
||||||
BMenuField *_modelField;
|
BMenuField *_modelField;
|
||||||
BPopUpMenu *_modelMenu;
|
BPopUpMenu *_modelMenu;
|
||||||
BButton *_sendButton;
|
BButton *_sendButton;
|
||||||
|
BMessage *settings;
|
||||||
|
|
||||||
// BMenuItem *fSaveMenuItem;
|
// BMenuItem *fSaveMenuItem;
|
||||||
};
|
};
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -33,6 +33,7 @@ APP_MIME_SIG = application/x-vnd.SLema-DumBer
|
||||||
# same name (source.c or source.cpp) are included from different directories.
|
# same name (source.c or source.cpp) are included from different directories.
|
||||||
# Also note that spaces in folder names do not work well with this Makefile.
|
# Also note that spaces in folder names do not work well with this Makefile.
|
||||||
SRCS = App.cpp \
|
SRCS = App.cpp \
|
||||||
|
Utils.cpp \
|
||||||
MainWindow.cpp \
|
MainWindow.cpp \
|
||||||
Conversation.cpp
|
Conversation.cpp
|
||||||
|
|
||||||
|
|
52
Utils.cpp
Normal file
52
Utils.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024, My Name <my@email.address>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include <File.h>
|
||||||
|
#include <Path.h>
|
||||||
|
#include <Directory.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <Message.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
|
status_t SaveMessageToFile(const BMessage& message, const char* fileName) {
|
||||||
|
BPath path;
|
||||||
|
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) {
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
path.Append(fileName);
|
||||||
|
|
||||||
|
BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
|
||||||
|
if (file.InitCheck() != B_OK) {
|
||||||
|
return file.InitCheck();
|
||||||
|
}
|
||||||
|
return message.Flatten(&file);
|
||||||
|
}
|
||||||
|
|
||||||
|
BMessage LoadMessageFromFile(const char* fileName) {
|
||||||
|
BPath path;
|
||||||
|
|
||||||
|
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) {
|
||||||
|
return BMessage(B_ERROR); // Return a message indicating an error
|
||||||
|
}
|
||||||
|
|
||||||
|
path.Append(fileName);
|
||||||
|
|
||||||
|
BFile file(path.Path(), B_READ_ONLY);
|
||||||
|
if (file.InitCheck() != B_OK) {
|
||||||
|
return BMessage(file.InitCheck()); // Return a message with the error status
|
||||||
|
}
|
||||||
|
|
||||||
|
BMessage message;
|
||||||
|
status_t status = message.Unflatten(&file);
|
||||||
|
if (status != B_OK) {
|
||||||
|
return BMessage(status); // Return a message indicating unflattening error
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
40
Utils.h
Normal file
40
Utils.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024, My Name <my@email.address>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
#include <Message.h>
|
||||||
|
#include <String.h>
|
||||||
|
#include <Path.h>
|
||||||
|
#include <Directory.h>
|
||||||
|
#include <File.h>
|
||||||
|
|
||||||
|
static const uint32 kShowStatus = 'stat';
|
||||||
|
static const uint32 kCheckKey = 'chkK';
|
||||||
|
static const uint32 kMsgNewFile = 'fNEW';
|
||||||
|
static const uint32 kMsgOpenFile = 'fOPN';
|
||||||
|
static const uint32 kMsgSaveFile = 'fSAV';
|
||||||
|
static const uint32 kModelSelected = 'mSEL';
|
||||||
|
static const uint32 kRequestModels = 'mREQ';
|
||||||
|
|
||||||
|
static const uint32 kViewJSON = 'vJSN';
|
||||||
|
static const uint32 kSettingsUpdate = 'sUPD';
|
||||||
|
static const char* kSettingsFileName = "bedumber_settings.bmessage";
|
||||||
|
|
||||||
|
|
||||||
|
static const uint32 kPulse = 'plse';
|
||||||
|
|
||||||
|
static const uint32 kSendPrompt = 'kspt';
|
||||||
|
static const uint32 kQuestionChanged = 'kqch';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
status_t SaveMessageToFile(const BMessage& message, const char* fileName);
|
||||||
|
BMessage LoadMessageFromFile(const char* fileName);
|
||||||
|
|
||||||
|
#endif // UTILS_H
|
Loading…
Add table
Add a link
Reference in a new issue