Create alert / message box in command line OSX app - c++

I'm trying to create a simple app that just opens an alert. So imagine this
int main(int argc, const char * argv[]) {
int result = SomeMagicAlertFunction("Hello World", "Yes", "No");
printf("User picked: %dĀ„n", result);
}
I've found some info about NSAlert but all the examples are for full OSX Apps, the kind with an app package as in
+-MyApp.app
|
+-Contents
|
+-MacOS
|
+-MyApp
etc, but I just want an alert in a command line app. One file, not an app package. Is that possible in OSX in either C/C++ or Objective C? I saw something about NSRunAlertPanel but that's been removed in Yosemite and says to use NSAlert.

Found an answer moments later
#import <Cocoa/Cocoa.h>
void SomeMagicAlertFunction(void) {
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:#"OK"];
[alert addButtonWithTitle:#"Cancel"];
[alert setMessageText:#"Delete the record?"];
[alert setInformativeText:#"Deleted records cannot be restored."];
[alert setAlertStyle:NSWarningAlertStyle];
if ([alert runModal] == NSAlertFirstButtonReturn) {
}
//[alert release];
}

Related

How to make NSOpenPanel accept keyboard and mouse events in objective-c?

I have C++ console application getting written in XCode, and I need to open a file selector dialog. To do this I'm using Cocoa with objective-c. I'm trying to open an NSOpenPanel to use it for this purpose. I'm using the following code currently:
const char* openDialog()
{
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseFiles:YES];
[openDlg setFloatingPanel:YES];
if ( [openDlg runModal] == NSOKButton )
{
for( NSURL* URL in [openDlg URLs] )
{
NSLog( #"%#", [URL path] );
return [URL.path UTF8String];
}
}
return NULL;
}
This works, however the created file selector doesnt accept mouse and keyboard events properly. It's hard to explain, but for example when I run the code from XCode when hovering above the window the mouse still behaves as if were in XCode, showing the caret symbol. When i run the application from the terminal whenever I type something it sends the input to the terminal, even though the file selector is "in front". Command clicking gives the mouse events properly to the file selector though.
I looked through NSOpenPanel's documentation and googled the problem extensively but I couldn't find an answer to this.
/*
To run in Terminal: clang openpanel.m -fobjc-arc -framework Cocoa -o openpanel && ./openpanel
*/
#import <Cocoa/Cocoa.h>
int main() {
NSApplication *application = [NSApplication sharedApplication];
[application setActivationPolicy:NSApplicationActivationPolicyAccessory];
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseFiles:YES];
[openDlg setFloatingPanel:YES];
if ( [openDlg runModal] == NSModalResponseOK ) {
for( NSURL* URL in [openDlg URLs] ) {
NSLog( #"%#", [URL path] );
}
}
return 0;
}
Thanks to #Willeke.

Non-expected result of QDir::homePath() for a program run by startproc

I have the following small program:
#include <unistd.h>
#include <pwd.h>
#include <QCoreApplication>
#include <QDir>
const char * homeDir()
{
return getpwuid(geteuid())->pw_dir;
}
int main(int argc, char *argv[])
{
printf("Qt homedir: %s\n", qPrintable(QDir::homePath()));
printf("Native homedir: %s\n", homeDir());
QCoreApplication a(argc, argv);
return a.exec();
}
Now:
when run directly by a "normal" user, ./program, the output is:
Qt homedir: /home/user
Native homedir: /home/usr
which is ok
when run directly by root, ./program, the output is:
Qt homedir: /root
Native homedir: /root
which is ok
when run by root as a different user by the means of sudo, e.g. sudo -u user ./program, the output is:
Qt homedir: /home/user
Native homedir: /home/user
which is ok
when run by root as a different user by the means of startproc, e.g. startproc -u user /full/path/to/program, the output is:
Qt homedir: /root
Native homedir: /home/user
which is NOT ok, or not expected (at least for me)
And my question is: why does the last run give a different result than the others? Is it a bug in Qt (doesn't take into account the fact, that the effective user is different than the real user, or something different), or am I missing some background info (e.g. the mechanism of how startproc works)?
The version of Qt in question is 5.6.1.
Qt's QFileSystemEngine uses the contents of the HOME environment variable on Unix - see its implementation. Yet startproc -u does not set HOME: that's why it fails.
The getpwuid call can be potentially very expensive and can block, i.e. by getting information from an LDAP or AD server, etc., and it's best if you take care of it yourself. Furthermore, it's not thread-safe, and you should use getpwuid_r instead.
An implementation might look as follows:
static QString getHomeDir() {
auto const N = sysconf(_SC_GETPW_R_SIZE_MAX);
auto *buffer = std::make_unique<char[]>(N);
passwd pwd;
passwd *result;
getpwuid_r(geteuid(), &pwd, buffer.get(), N, &result);
if (result) {
auto *dir = result->pw_dir;
auto const decoded = QFile::decodeName(dir);
return QDir::cleanPath(decoded);
}
return {};
}
enum class HomeDir { Default, Init };
QString homeDir(HomeDir option = HomeDir::Default) {
// needs a C++11 compiler for thread-safe initialization
static QFuture<QString> home = QtConcurrent::run(getHomeDir);
return (option == HomeDir::Init) ? QString() : home;
};
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
homeDir(HomeDir::Init);
// do other time-consuming initializations here
QString () << homeDir();
}

Qt - Dialog in a DLL

In my company, we are developing with Embarcadero-C++-IDE (which is very uncomfortable). To start moving away, we port individual dialogs in a dll to Qt. My qt-dll-code Looks like this for example
extern "C" ROBOTECHPOLYLINEDIALOGSHARED_EXPORT void popupRoboTechDialog()
{
if( ! QApplication::instance() )
{
int argc = 1;
char *argv[] = {"Design polyline"};
QApplication app(argc, argv);
RoboTechPolyline dialog;
dialog.show();
app.exec();
}
else
{
RoboTechPolyline Dialog;
Dialog.exec();
}
}
Trying to start the Dialog from another thread like here Starting Qt GUI from dll (in DLLStart function) did make my Dialog unresponsive, but I don't think the question and mine relate too much.
I'm loading this Dll dynamically from the main-application and it works fine. However, when I make the Dialog Pop up a second time I get an "Access Violation at address .. in module MSVCR110D.dll" and on the third time, I get "ASSERT failure in QCoreApplication , there should be only one application object". So I always Need to Close the whole application in order to make the Dialog appear a second time, which greaty slows down work.
If I add at the bottom the line
QApplication::quit()
the Dialog appears a second time, but the Programm crashes on closing this second Dialog.
The code to load the dll is as follows
HINSTANCE lib = ::LoadLibrary(L"RoboTechPolylineDialog.dll");
if(!lib)
{
ShowMessage("Unable to load RoboTechPolylineDialog.dll");
return;
}
typedef void ( *POPUP_ROBO_TECH_DIALOG )();
POPUP_ROBO_TECH_DIALOG fp = (POPUP_ROBO_TECH_DIALOG) ::GetProcAddress(lib, "popupRoboTechDialog");
if(!fp)
{
ShowMessage("Unable to load function popupRoboTechDialog from RoboTechPolylineDialog.dll");
::FreeLibrary(lib);
return;
}
(*fp)( );
FreeLibrary(lib);
So why am I constructing more than one QApplication at a time? I can in above code replace the line
(*fp)();
with
(*fp)();
(*fp)();
and the Dialog appears twice and everything works greatly. But how can the call to ::FreeLibrary(lib) make things fail.
Can anyone help me? Any help, Workarounds, etc.. is appreciated.
This should work:
#include <QApplication>
#include <QString>
#include <QDialog>
class App {
QApplication *_app;
public:
App(int argc = 0, char** argv = NULL)
: _app(new QApplication(argc, argv))
{
}
~App() {
delete _app;
}
};
void dialog()
{
static int argc = 1;
static char *argv[] = {"Design polyline"};
static App(argc, argv);
QDialog dlg;
dlg.exec();
}
void main()
{
dialog();
dialog();
dialog();
}
Another advice: load Qt libs from as subpath since you could find dll conflict with other apps using it on the same folder (personal experience)

How to force QLocale::system to change

I need to test my application translation to non-English language very often, and this is very uncomfortable to change the whole operating system language just to do this simple check.
How can i change Qt-detected system language using environment variables for example?
or with command-line parameter.
I try to change LANG, LANGUAGE environment variables, but it has no effect.
However, under GNOME it has!
UPD: code i'm using such code to determine the system locale and load appropriate translation:
QTranslator app_translator;
if (!app_translator.load ("app_" + QLocale::system ().name (), app_tr_dir))
qWarning ("Can't load app translator file for locale %s from %s", qPrintable (QLocale::system ().name ()), app_tr_dir.toLocal8Bit().data());
else
app.installTranslator (&app_translator);
P.S. My OS is Kubuntu 13.10, Qt version is 4.8.
You can always change the locale by QLocale::setDefault() method. here's an example from one project:
void Language::setCurrentLanguage(Language::Languages language)
{
if (language == Language::Arabic) {
QLocale l(QLocale::Arabic, QLocale::SaudiArabia);
QLocale::setDefault(l);
dynamic_cast<MangoApp*>(qApp)->setLayoutDirection(Qt::RightToLeft);
dynamic_cast<MangoApp*>(qApp)->removeAllTranslator();
dynamic_cast<MangoApp*>(qApp)->loadQtTranslator();
dynamic_cast<MangoApp*>(qApp)->loadMangoTranslator();
} else {
QLocale l(QLocale::English, QLocale::UnitedStates);
QLocale::setDefault(l);
dynamic_cast<MangoApp*>(qApp)->setLayoutDirection(Qt::LeftToRight);
dynamic_cast<MangoApp*>(qApp)->removeAllTranslator();
}
}
For testing you can use something like that (just correct main function):
int main(int argc, char **argv) {
QApplication app(argc, argv);
QLocale localeUsedToDeterminateTranslators = QLocale::system();
Q_FOREACH(QString a, app.arguments()) {
const static localeParam = "-locale:";
if (a.startsWith(localeParam)) {
localeUsedToDeterminateTranslators = QLocale(a.mid(sizeof(localeParam)-1));
break;
}
}
... // your normal code
Then when you run you app you can just run it with extra parameter: ./yourAppName -locale:nl. See documentation of QLocale for possible values.
Edit: I've found even better approach, there is a method QLocale::setDefault, so this should work even better:
int main(int argc, char **argv) {
QApplication app(argc, argv);
Q_FOREACH(QString a, app.arguments()) {
const static localeParam = "-locale:";
if (a.startsWith(localeParam)) {
QLocale::setDefault(QLocale(a.mid(sizeof(localeParam)-1)));
break;
}
}
...
QTranslator app_translator;
if (!app_translator.load ("app_" + QLocale().name (), app_tr_dir))
qWarning ("Can't load app translator file for locale %s from %s", qPrintable (QLocale().name()), app_tr_dir.toLocal8Bit().data());
else
app.installTranslator (&app_translator);
Using the LANGUAGE (not LANG) environment variable should definitely change the value returned by QLocale::system().name(), because this environment variable has precedence over all other ways to define the application message locale (details).
I tested it this with Qt 5.12 under Lubuntu 19.10 (means, using the LXQt desktop), and it works. The command was:
LANGUAGE=de ./application
If this really does not work under Kubuntu, it should be reported as a bug, because then Kubuntu is interfering with the way how an application is told its locale.

C++ Find execution path on Mac

Lets say my executable is located at /Users/test_user/app on Mac OSX and I am running it from /Users/test_user/Desktop/run_app:
Desktop run_app$ /Users/test_user/app/exec
Within my C++ code how can I find the path to the location of the executable (which in this case would be /users/test_user/app)? I need to reference some other files at this path within my code and do not want to put absolute paths within the code as some users might place the folder in a different location.
man 3 dyld says:
_NSGetExecutablePath() copies the path of the main executable into the
buffer buf. The bufsize parameter should initially be the size of the
buffer. This function returns 0 if the path was successfully copied.
It returns -1 if the buffer is not large enough, and * bufsize is set
to the size required. Note that _NSGetExecutablePath() will return "a
path" to the executable not a "real path" to the executable. That is,
the path may be a symbolic link and not the real file. With deep
directories the total bufsize needed could be more than MAXPATHLEN.
#include <mach-o/dyld.h>
#include <limits.h>
int main(int argc, char **argv)
{
char buf [PATH_MAX];
uint32_t bufsize = PATH_MAX;
if(!_NSGetExecutablePath(buf, &bufsize))
puts(buf);
return 0;
}
Provided I'm understanding you correctly, you should be able to use NSProcessInfo's -argumentsĀ method to get the executable path.
To mix in Objective-C code with C++ code, you can just change the filename extension of the source file in question from .cpp to .mm. Then add the Foundation.framework to the Link Binary With Library build phase of your Target.
[EDIT] updated to show the difference between argv[0] and [[[NSProcessInfo processInfo] arguments] objectAtIndex:0].
Then to use the code, you could do something like in the following code:
#include <iostream>
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// print out raw args
NSMutableArray *arguments = [NSMutableArray array];
for (NSUInteger i = 0; i < argc; i++) {
NSString *argument = [NSString stringWithUTF8String:argv[i]];
if (argument) [arguments addObject:argument];
}
NSLog(#"arguments == %#", arguments);
const char *executablePath =
[[[[NSProcessInfo processInfo] arguments] objectAtIndex:0]
fileSystemRepresentation];
printf("executablePath == %s\n", executablePath);
const char *executableDir =
[[[[[NSProcessInfo processInfo] arguments] objectAtIndex:0]
stringByDeletingLastPathComponent] fileSystemRepresentation];
printf("executableDir == %s\n", executableDir);
[pool release];
return 0;
}
If I then cd into the parent directory of the executable, and then execute the executable using a relative path:
MacPro:~ mdouma46$ cd /Users/mdouma46/Library/Developer/Xcode/DerivedData/executablePath-ewememtbkdajumdlpnciyymduoah/Build/Products/Debug
MacPro:Debug mdouma46$ ./executablePath blah blah2
I get the following output:
2011-08-10 12:59:52.161 executablePath[43554:707] arguments == (
"./executablePath",
blah,
blah2
)
executablePath == /Users/mdouma46/Library/Developer/Xcode/DerivedData/executablePath-ewememtbkdajumdlpnciyymduoah/Build/Products/Debug/executablePath
executableDir == /Users/mdouma46/Library/Developer/Xcode/DerivedData/executablePath-ewememtbkdajumdlpnciyymduoah/Build/Products/Debug
So, while argv[0] may not necessarily be a full path, the result returned from [[[NSProcessInfo processInfo] arguments] objectAtIndex:0] will be.
So, there's [[[NSProcessInfo processInfo] arguments] objectAtIndex:0], or a slightly simpler approach is just to use NSBundle itself, even if it is a command-line tool (see What is the "main bundle" of a command-line foundation tool?):
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
const char *executablePath =
[[[NSBundle mainBundle] executablePath] fileSystemRepresentation];
[pool release];
Maybe you could use the which command. http://unixhelp.ed.ac.uk/CGI/man-cgi?which
If it's a "real" OS X application the proper way to do it is to create a bundle:
http://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFBundleRef/Reference/reference.html