Using C, how to pass a string to the Smoke C++ API - c++

Smoke provides an introspective C++ API wrapper to the Qt framework.
https://techbase.kde.org/Development/Languages/Smoke
https://techbase.kde.org/Development/Languages/Smoke/API_Documentation
SmokeC wraps the the Smoke wrapper with C functions to facilitate using the Smoke API from C.
https://github.com/pankajp/pysmoke/blob/master/smokec/smokec.cpp
Working example application in C (functionally identical to the C++ example in the first link above):
https://github.com/pankajp/pysmoke/blob/master/examples/hellowidget.c
I'm trying to modify this example application and add further calls to QWidget object methods, but I can't figure out how to call a method that takes a QString argument, such as setWindowTitle, whose signature is
void setWindowTitle(const QString &);
Here's my best guess:
/* Set the window title... Segfaults */
methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowTitle$");
klass = Smoke_classes(classId.smoke)[classId.index];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
stack[1].s_voidp = (void*)"hello universe";
(*klass.classFn)(meth.method, widget, stack);
... but this segfaults. Can anyone advise how I should construct a QString argument using the SmokeC wrapper?
My complete modified example application follows.
/*
* File: hellowidget.c
* Author: pankaj
*
* Created on November 19, 2013, 2:41 PM
*/
#include <stdio.h>
#include <string.h>
#include "smokec.h"
#include "bindings.h"
/*
* In a bindings runtime, this should return the classname as used
* in the bindings language, e.g. Qt::Widget in Ruby or
* Qyoto.QWidget in C# or QtGui.QWidget in python
*/
char *className(CSmokeBinding binding, Index classId) {
return (char*) Smoke_classes(CSmoke_FromBinding(binding))[classId].className;
}
void deleted(CSmokeBinding binding, Index classId, void *obj)
{
}
cbool callMethod(CSmokeBinding binding, Index method, void *obj, Stack args, cbool isAbstract)
{
return 0;
}
int main(int argc, char **argv)
{
/* Initialize the Qt SMOKE runtime. */
init_qtcore_CSmoke();
init_qtgui_CSmoke();
CSmoke qtcore_smoke = qtcore_CSmoke();
CSmoke qtgui_smoke = qtgui_CSmoke();
/* Create a SmokeBinding for the Qt SMOKE runtime. */
CSmokeBinding qtcoreBinding = SmokeBinding_new(qtcore_smoke, deleted, callMethod, className);
CSmokeBinding qtguiBinding = SmokeBinding_new(qtgui_smoke, deleted, callMethod, className);
/* Find the 'QApplication' class. */
CModuleIndex classId = findClass("QApplication");
/* find the methodId. we use a munged method signature, where
* $ is a plain scalar
* # is an object
* ? is a non-scalar (reference to array or hash, undef) */
CModuleIndex methId = Smoke_findMethod(classId.smoke, "QApplication", "QApplication$?"); // find the constructor
/* Get the Smoke::Class */
Class klass = Smoke_classes(classId.smoke)[classId.index];
// findMethod() returns an index into methodMaps, which has
// information about the classId, methodNameId and methodId. we
// are interested in the methodId to get a Smoke::Method
Method meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
StackItem stack[3];
// QApplication expects a reference to argc, so we pass it as a pointer
stack[1].s_voidp = &argc;
stack[2].s_voidp = argv;
// call the constructor, Smoke::Method::method is the methodId
// specifically for this class.
(*klass.classFn)(meth.method, 0, stack);
// the zeroth element contains the return value, in this case the
// QApplication instance
void *qapp = stack[0].s_voidp;
// method index 0 is always "set smoke binding" - needed for
// virtual method callbacks etc.
stack[1].s_voidp = qtguiBinding.binding;
(*klass.classFn)(0, qapp, stack);
// create a widget
classId = findClass("QWidget");
methId = Smoke_findMethod(classId.smoke, "QWidget", "QWidget");
klass = Smoke_classes(classId.smoke)[classId.index];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, 0, stack);
void *widget = stack[0].s_voidp;
// set the smoke binding
stack[1].s_voidp = qtguiBinding.binding;
(*klass.classFn)(0, widget, stack);
/* Show the widget maximized.*/
methId = Smoke_findMethod(classId.smoke, "QWidget", "showMaximized");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, widget, 0);
/* Raise the window to the foreground */
methId = Smoke_findMethod(classId.smoke, "QWidget", "raise");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, widget, 0);
/* Set the modified indicator. */
methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowModified$");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
stack[1].s_bool = 1;
(*klass.classFn)(meth.method, widget, stack);
/* Set the window title... Segfaults */
methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowTitle$");
klass = Smoke_classes(classId.smoke)[classId.index];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
stack[1].s_voidp = (void*)"hello universe";
(*klass.classFn)(meth.method, widget, stack);
// we don't even need findClass() when we use the classId provided
// by the MethodMap
methId = Smoke_findMethod(qtgui_smoke, "QApplication", "exec");
klass = Smoke_classes(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].classId];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
// call QApplication::exec()
(*klass.classFn)(meth.method, 0, stack);
// store the return value of QApplication::exec()
int retval = stack[0].s_int;
// destroy the QApplication instance
methId = Smoke_findMethod(qtgui_smoke, "QApplication", "~QApplication");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, qapp, 0);
// destroy the smoke instance
CSmoke_delete(qtgui_smoke);
CSmoke_delete(qtcore_smoke);
// return the previously stored value
return retval;
}

Related

How do I get all the clipboard events provided by XCB?

I am currently working on a cross-host clipboard sharing tool that can share text, rich text, or files.
Based on what I've learned so far, I already know which X11 events need to be handled, but I can't listen to them.
My current code I can only catch events that select text.
how to get the clipboard change event caused by ctrl+c?
#include <QCoreApplication>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
/**
* Enumeration of standard X11 atom identifiers
*/
typedef enum std_x_atoms {
/** The TARGETS atom identifier **/
X_ATOM_TARGETS = 0,
/** The MULTIPLE atom identifier **/
X_ATOM_MULTIPLE,
/** The TIMESTAMP atom identifier **/
X_ATOM_TIMESTAMP,
/** The INCR atom identifier **/
X_ATOM_INCR,
/** The CLIPBOARD atom identifier **/
X_ATOM_CLIPBOARD,
/** The UTF8_STRING atom identifier **/
X_ATOM_UTF8_STRING,
X_ATOM_XCLIPD,
/** End marker sentinel **/
X_ATOM_END
} std_x_atoms;
/**
* Union to simplify getting interned atoms from XCB
*/
typedef union atom_c {
/** The atom **/
xcb_atom_t atom;
/** The cookie returned by xcb_intern_atom **/
xcb_intern_atom_cookie_t cookie;
} atom_c;
/**
* The standard atom names. These values should match the
* std_x_atoms enumeration.
*/
const char * const g_std_atom_names[X_ATOM_END] = {
"TARGETS", "MULTIPLE", "TIMESTAMP", "INCR",
"CLIPBOARD", "UTF8_STRING", "XCLIPD"
};
atom_c std_atoms[X_ATOM_END];
/**
* \brief Interns the list of atoms
*
* \param [in] xc The XCB connection.
* \param [out] atoms The location to store interned atoms.
* \param [in] atom_names The names of the atoms to intern.
* \param [in] number The number of atoms to intern.
* \return true iff all atoms were interned.
*/
static bool x11_intern_atoms(xcb_connection_t *xc, atom_c *atoms, const char * const *atom_names, int number) {
for (int i = 0; i < number; i++) {
atoms[i].cookie = xcb_intern_atom(xc, 0,
strlen(atom_names[i]), atom_names[i]);
}
for (int i = 0; i < number; i++) {
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xc,
atoms[i].cookie, NULL);
if (reply == NULL) {
return false;
}
atoms[i].atom = reply->atom;
free(reply); /* XCB: Do not use custom allocators */
}
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_generic_event_t *e;
uint32_t mask = 0;
uint32_t values[2];
/* Create the window */
c = xcb_connect (NULL, NULL);
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
if (!x11_intern_atoms(c, std_atoms, g_std_atom_names, X_ATOM_END))
return 1;
// xcb_flush (c);
win = xcb_generate_id (c);
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
values[0] = screen->white_pixel;
values[1] = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY;;
xcb_create_window (c, /* Connection */
XCB_COPY_FROM_PARENT, /* depth */
win, /* window Id */
screen->root, /* parent window */
0, 0, /* x, y */
150, 150, /* width, height */
10, /* border_width */
XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
screen->root_visual, /* visual */
mask, values); /* masks */
xcb_map_window (c, win);
xcb_flush (c);
// init xfixes
xcb_generic_error_t *error = 0;
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(c, &xcb_xfixes_id);
if (!reply || !reply->present) {
return -1;
}
xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(c,
XCB_XFIXES_MAJOR_VERSION,
XCB_XFIXES_MINOR_VERSION);
xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (c,
xfixes_query_cookie, &error);
if (!xfixes_query || error || xfixes_query->major_version < 2) {
free(error);
}
free(xfixes_query);
// delivers request
mask = XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE
| XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY
| XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER;
xcb_xfixes_select_selection_input_checked(c, win, XCB_ATOM_NONE, mask);
xcb_flush(c);
// recevie events
uint response_type;
while (e = xcb_wait_for_event(c)) {
xcb_xfixes_selection_notify_event_t *notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(e);
printf("response_type = %d\n", response_type);
response_type = notify_event->response_type & ~0x80;
printf("response_type = %d\n", response_type);
if (response_type == reply->first_event + XCB_XFIXES_SELECTION_NOTIFY) {
printf("notify\n");
} else {
printf("code:%d\n", response_type);
}
}
return a.exec();
}

How to change Gtk::Image after object creation when PixBuf is given

I am attempting to change Gtk::Image-derived object by giving it pixbuf, but i cannot figure out how to approach that.
The simple setup can be mimicked as:
#include <gtkmm.h>
#include <iostream>
class MyImage : public Gtk::Image
{
public:
void setPixBuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf);
};
void MyImage::setPixBuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
{
// How can i override the existing pixbuf here?
}
void freeImagePixelData(const guint8* data)
{
delete[] data;
}
Glib::RefPtr<Gdk::Pixbuf> generateTestImage()
{
guint8 *data = new guint8[40*40*4];
for(int i=0; i<40*40*4; )
{
data[i++] = (guint8)255; // R
data[i++] = (guint8)255; // G
data[i++] = (guint8)0; // B
data[i++] = (guint8)255; // A
}
Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_data(
data, Gdk::Colorspace::COLORSPACE_RGB, true, 8, 40, 40, 40*4, sigc::ptr_fun(&freeImagePixelData));
return pixbuf;
}
int main(int argc, char** argv)
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "com.almost-university.gtkmm.image.pixbuf");
Gtk::Window window;
MyImage im1;
im1.setPixBuf(generateTestImage());
window.add(im1);
window.show_all_children();
app->run(window);
return 0;
}
(Please note that this is an oversimplified version of what i am trying to figure out, i do know that i should be using Gtk::manage and not add things directly to the window without another container, this is just a mock-up).
I know that if i were to generate the image using a constructor as so:
Gtk::Image im2(generateTestImage());
window.add(im2);
then i would in fact be getting a yellow square.
Somehow i refuse to believe that one can only use pixbuf at the time of object creation. There must be a way to set the image data somehow, and i just cannot find the needed function.
To set Pixbuf in an Gtk::Image you can use Gtk::Image::set(const Glib::RefPtr< Gdk::Pixbuf >& pixbuf) method:
void MyImage::setPixBuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
{
set(pixbuf);
}

How can I return to main in Qt?

I've been working on a project recently which is a simple game. I've written the following code in my main function (file main.cpp):
ending_note = "Draw.";
End_Page end(ending_note, a);
end.show();
(*a).exec();
if(end.flag == 1)
{
return 1;
} //end if
where a is a Qapplication object. The class End_Page is defined as follows (file end_page.cpp):
End_Page::End_Page(string _winner, QApplication* _a, QWidget *parent):QWidget(parent){
a = _a;
this->setFixedSize(900, 675);
this->move(350, 50);
flag = 0;
//------------------- background label
background = new QLabel(this);
QMovie* movie2 = new QMovie("..\\project\\Data\\pic\\7.jpeg");
movie2->setScaledSize(QSize(this->width(), 600));
background->setMovie(movie2);
background->setGeometry(0, 0, this->width(), 600);
movie2->start();
//-------------------- set label
QString s;
label = new QLabel(s.fromStdString(_winner), this);
label->setStyleSheet("QLabel { color : rgb(200, 0, 30); qproperty-alignment: AlignCenter; }");
QFont f( "MV Boli", 32, QFont::Bold);
label->setFont(f);
label->setGeometry(0,this->height() - 400, this->width(), 160);
question = new QLabel("Do you want to play again?\n", this);
question->setStyleSheet("QLabel { color : black;}");
question->setGeometry(375, 610, 200, 30);
accept = new QPushButton("Yes", this);
accept->setGeometry(300, 630, 80, 40);
decline = new QPushButton("No", this);
decline->setGeometry(500, 630, 80, 40);
//-------------------- connect
connect(this,SIGNAL(closeSignal()), this, SLOT(closeProgram()));
connect(decline, SIGNAL(clicked()), this, SLOT(closeProgram()));
connect(accept, SIGNAL(clicked()), this, SLOT(restartProgram()));
}
End_Page::~End_Page(){}
void End_Page::closeEvent(QCloseEvent* event){
emit closeSignal();
event->accept();
}
void End_Page::EndGame(){
a->exit();
}
void End_Page::closeProgram(){
exit(0);
}
void End_Page::restartProgram(){
flag = 1;
a->exit();
}
My problem is that, after the program executes the statement (*a).exec();, if the user click on the push button labeled Yes, the program executes the function restartProgram to the end, but after that it doesn't continue back in function main (in other words it gets stuck there). How can I solve this problem?
Try calling quit() or exit() as static class members (you don't need to pass your QApplication around):
For any GUI application using Qt, there is precisely one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time. For non-GUI Qt applications, use QCoreApplication instead, as it does not depend on the QtGui library.
The QApplication object is accessible through the instance() function that returns a pointer equivalent to the global qApp pointer.
void End_Page::restartProgram(){
flag = 1;
QApplication::quit();
}
But, the main problem in your application is that you are emmiting closeSignal() in your closeEvent() and the slot connected to it will call exit(0); system call, which is, I think, completely unnecessary, and which will "kill" the current process.
Here is a completely working example:
#include <QApplication>
#include <qtimer>
#include <iostream>
/* Move this into h file and moc it! */
class Window:public QObject
{
Q_OBJECT
public slots:
void closeApp(){ QApplication::quit(); flag = 500; }
public:
int flag;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window win;
QTimer::singleShot(5000, &win, SLOT(closeApp()));
a.exec();
std::cout << "Flag: " << win.flag << std::endl;
return 0;
}
Edit
Why are you doing this:
if(end.flag == 1) // flag is set to 1 in restartProgram slot
{
return 1;
} //end if
This will exit your main() function, and it will not restart program.

GTK Entry to Integer conversion

How do you get the text from a gtk entry widget and then convert that to an integer value. Notice in my code I include a wrapper struct called Window that contains pointers to widgets. In the main, I declare an instance of a Window and then build the correct widgets with the appropriate GTK function calls. I then pass that window object to the function that handles the clicked action. I want to then calculate the numerator divided by the denominator in integer format. Below is my attempt. All the code works except for the button_clicked function. Any ideas?
#include <gtk/gtk.h>
#include <stdlib.h>
struct Window
{
GtkWidget *numerator;
GtkWidget *denominator;
GtkWidget *button;
GtkWidget *label;
};
void button_clicked(GtkWidget *widget, gpointer data)
{
Window* w = (Window*)data;
char buf[10];
char buffer[200];
GtkEntry* e = (GtkEntry*)w->numerator;
const gchar* entry1 = gtk_entry_get_text(e);
char* test = (char*)gchar;
int r = atoi(test);
sprintf(buf,"%d",r);
GtkWidget *label = w->label;
gtk_label_set_text(GTK_LABEL(label), buf);
}
int main(int argc, char*argv[])
{
GtkWidget *window;
GtkWidget *table;
Window w;
//Set up my window
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Division");
gtk_window_set_default_size(GTK_WINDOW(window),500,500);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
//Create my table and add it to the window
table = gtk_table_new(4,2,FALSE);
gtk_container_add(GTK_CONTAINER(window),table);
//Create instances of all my widgets
w.numerator = gtk_entry_new();
w.denominator = gtk_entry_new();
w.button = gtk_button_new_with_label("Click");
w.label = gtk_label_new("result");
//Attack the widgets to the table
gtk_table_attach(GTK_TABLE(table), w.numerator,0,1,0,1,GTK_FILL,GTK_FILL,5,5);
gtk_table_attach(GTK_TABLE(table), w.denominator,0,1,1,2,GTK_FILL,GTK_FILL,5,5);
gtk_table_attach(GTK_TABLE(table), w.button,0,1,2,3,GTK_FILL,GTK_FILL,5,5);
gtk_table_attach(GTK_TABLE(table), w.label,0,1,3,4,GTK_FILL,GTK_FILL,5,5);
//attach the click action to with the button to invoke the button_clicked function
g_signal_connect(G_OBJECT(w.button),"clicked",G_CALLBACK(button_clicked),&w);
g_signal_connect_swapped(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
If I see this correctly, in your "test code" all you're trying to do is set the label string to the contents of "w->numerator", right?
The line
char* test = (char*)gchar;
looks fishy to me and doesn't even compile, it looks like a typo.
Change the "gchar" to "entry1", and it should do what you want it to.
I have a recommendation for you though: use GtkSpinButton instead of GtkEntry. It is like a custom Entry made for numerical values, and the retrieval of such is many times easier.

(C++) MessageBox for Linux like in MS Windows

I need to implement a simple graphical message box for a Linux (SDL) application similar to the Windows MessageBox in C++ (gcc/g++ 4.4.0). All it needs to do is to display a caption, a message and an ok or close button and to return to the calling function when that button is clicked.
SDL just uses X(11) to open a window for (OpenGL) rendering.
I have looked through a similar thread regarding a GTK implementation, but that implementation doesn't seem to work properly.
I have also tried wxWidgets' wxMessageBox function, but compiling the headers makes the compiler throw error messages about syntax errors in include/c++/4.4.0/bits/stl_algobase.h (gcc 4.4.0 32 bits on openSuSE 11.1 32 bits). Using wxWidgets also means having to link a ton of libraries, adding STL to my app (Which it doesn't need otherwise) and who knows what else, so I do not want to use wxWidgets.
X11/motif (openmotif) has what I need (XmCreate{Error|Warning|InfoDialog), but these need a parent widget (e.g. top level window) which I don't have and do not accept a NULL parameter for these.
So I am stumped right now. Is there a simple way to do what I want? Or at least a halfway simple/easy/straightforward one? If yes, which one (giving as many details as possible would be highly appreciated).
In SDL2, you can now show message boxes:
http://wiki.libsdl.org/SDL_ShowSimpleMessageBox
int SDL_ShowSimpleMessageBox(Uint32 flags,
const char* title,
const char* message,
SDL_Window* window)
http://wiki.libsdl.org/SDL_ShowMessageBox
int SDL_ShowMessageBox(const SDL_MessageBoxData* messageboxdata,
int* buttonid)
I personally use Qt4's QMessageBox.
example:
QMessageBox mb(QMessageBox::Question, "Title", "Message", QMessageBox::Ok | QMessageBox::Cancel);
if(mb.exec() == QMessageBox::Ok) { do_stuff(); }
Looks like you will have to create a top-level X11/Motif window. Here's some code to get you started:
#include <Xm/Xm.h>
#include <Xm/PushB.h>
/* Prototype Callback function */
void pushed_fn(Widget , XtPointer ,
XmPushButtonCallbackStruct *);
main(int argc, char **argv)
{ Widget top_wid, button;
XtAppContext app;
top_wid = XtVaAppInitialize(&app, "Push", NULL, 0,
&argc, argv, NULL, NULL);
button = XmCreatePushButton(top_wid, "Push_me", NULL, 0);
/* tell Xt to manage button */
XtManageChild(button);
/* attach fn to widget */
XtAddCallback(button, XmNactivateCallback, pushed_fn, NULL);
XtRealizeWidget(top_wid); /* display widget hierarchy */
XtAppMainLoop(app); /* enter processing loop */
}
void pushed_fn(Widget w, XtPointer client_data,
XmPushButtonCallbackStruct *cbs)
{
printf("Don't Push Me!!\n");
}
This was copied from here which might give you some more pointers on this.
Here is my solution. I chose to use Motif (OpenMotif) as it requires comparably few extra libraries (Xm, Xt, X11). Depending on the message size, my implementation opens a simple message box or a more sophisticated dialog with a non editable, scrollable text (the latter taken from the Motif programmer's manual and adapted for my purposes).
Include files and global data:
#include <Xm/Xm.h>
#include <Xm/MwmUtil.h>
#include <Xm/MainW.h>
#include <Xm/CascadeB.h>
#include <Xm/MessageB.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/PushBG.h>
#include <Xm/LabelG.h>
#include <Xm/PanedW.h>
#include <Xm/Text.h>
#include <Xm/DialogS.h>
#include <Xm/Command.h>
static XtAppContext appShell;
Helper function to determine rows and max. cols of a text message:
static int MsgSize (char* pszMsg, int& nCols)
{
if (!(pszMsg && *pszMsg))
return 0;
int nRows = 1;
nCols = 0;
for (char* p = pszMsg; *p && (pszMsg = strchr (p, '\n')); nRows++, p = ++pszMsg) {
if (nCols < pszMsg - p)
nCols = pszMsg - p;
}
return nRows;
}
Callback function for the message dialog's close button:
void DestroyShell (Widget widget, XtPointer clientData, XtPointer callData)
{
Widget shell = (Widget) clientData;
XtDestroyWidget (shell);
// tell the application event loop to terminate w/o terminating the application
XtAppSetExitFlag (appShell);
}
Build a dialog containing a scrollable, non editable text widget and a close button. Taken from the Motif programmer's manual and slightly adapted (no icon, single button), minimal window decoration).
void XmMessageDialog (const char* pszMsg, int nRows, int nCols, bool bError)
{
Widget msgBox, pane, msgText, form, widget;
void DestroyShell(Widget, XtPointer, XtPointer);
Arg args [10];
int n = 0;
int i;
Dimension h;
// Set up a DialogShell as a popup window. Set the delete window protocol response to XmDESTROY to make sure that
// the window goes away appropriately. Otherwise, it's XmUNMAP which means it'd be lost forever, since we're not storing
// the widget globally or statically to this function.
Widget topWid = XtVaAppInitialize (&appShell, "D2X-XL", NULL, 0, &argc, argv, NULL, NULL);
XtSetArg (args [0], XmNdeleteResponse, XmDESTROY);
msgBox = XmCreateDialogShell (topWid, bError ? const_cast<char*>("Error") : const_cast<char*>("Warning"), args, 1);
XtVaGetValues (msgBox, XmNmwmDecorations, &i, NULL);
i &= ~(MWM_DECOR_ALL | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE | MWM_DECOR_MENU);
XtVaSetValues (msgBox, XmNmwmDecorations, i, NULL);
XtVaGetValues (msgBox, XmNmwmFunctions, &i, NULL);
i &= ~(MWM_FUNC_ALL | MWM_FUNC_MINIMIZE | MWM_FUNC_MAXIMIZE | MWM_FUNC_CLOSE);
XtVaSetValues (msgBox, XmNmwmFunctions, i, NULL);
// Create a PanedWindow to manage the stuff in this dialog. PanedWindow won't let us set these to 0!
XtSetArg (args [0], XmNsashWidth, 1);
// Make small so user doesn't try to resize
XtSetArg (args [1], XmNsashHeight, 1);
pane = XmCreatePanedWindow (msgBox, const_cast<char*>("pane"), args, 2);
// Create a RowColumn in the form for Label and Text widgets. This is the control area.
form = XmCreateForm (pane, const_cast<char*>("form1"), NULL, 0);
// prepare the text for display in the ScrolledText object we are about to create.
n = 0;
XtSetArg (args [n], XmNscrollVertical, True); n++;
XtSetArg (args [n], XmNscrollHorizontal, False); n++;
XtSetArg (args [n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
XtSetArg (args [n], XmNeditable, False); n++;
XtSetArg (args [n], XmNcursorPositionVisible, False); n++;
XtSetArg (args [n], XmNwordWrap, True); n++;
XtSetArg (args [n], XmNvalue, pszMsg); n++;
XtSetArg (args [n], XmNrows, min (nRows, 30)); n++;
XtSetArg (args [n], XmNcolumns, min (nCols, 120)); n++;
msgText = XmCreateScrolledText (form, const_cast<char*>("help_text"), args, n);
// Attachment values must be set on the Text widget's PARENT, the ScrolledWindow. This is the object that is positioned.
XtVaSetValues (XtParent (msgText),
XmNleftAttachment, XmATTACH_FORM,
XmNtopAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
NULL);
XtManageChild (msgText);
XtManageChild (form);
// Create another form to act as the action area for the dialog
XtSetArg (args [0], XmNfractionBase, 5);
form = XmCreateForm (pane, const_cast<char*>("form2"), args, 1);
// The OK button is under the pane's separator and is attached to the left edge of the form. It spreads from
// position 0 to 1 along the bottom (the form is split into 5 separate grids via XmNfractionBase upon creation).
widget = XmCreatePushButtonGadget (form, const_cast<char*>("Close"), NULL, 0);
XtVaSetValues (widget,
XmNtopAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
XmNleftAttachment, XmATTACH_POSITION,
XmNleftPosition, 2,
XmNrightAttachment, XmATTACH_POSITION,
XmNrightPosition, 3,
XmNshowAsDefault, True,
XmNdefaultButtonShadowThickness, 1,
NULL);
XtManageChild (widget);
XtAddCallback (widget, XmNactivateCallback, DestroyShell, (XtPointer) msgBox);
// Fix the action area pane to its current height -- never let it resize
XtManageChild (form);
XtVaGetValues (widget, XmNheight, &h, NULL);
XtVaSetValues (form, XmNpaneMaximum, h, XmNpaneMinimum, h, NULL);
// This also pops up the dialog, as it is the child of a DialogShell
XtManageChild (pane);
}
Callback function for the message box' Ok button
void XmCloseMsgBox (Widget w, XtPointer clientData, XtPointer callData)
{
XtAppSetExitFlag (appShell);
}
Decide whether to use the simple or advanced message box, display either of them, and remove them when the user clicks their close/ok button.
void XmMessageBox (const char* pszMsg, bool bError)
{
Widget topWid;
int nRows, nCols;
nRows = MsgSize (const_cast<char*>(pszMsg), nCols);
if ((nRows > 3) || (nCols > 360))
XmMessageDialog (pszMsg, nRows, nCols, bError);
else { // use the built-in message box
topWid = XtVaAppInitialize (&appShell, "D2X-XL", NULL, 0, &argC, argv, NULL, NULL);
// setup message box text
Arg args [1];
XmString xmString = XmStringCreateLocalized (const_cast<char*>(pszMsg));
XtSetArg (args [0], XmNmessageString, xmString);
// create and label message box
Widget xMsgBox = bError
? XmCreateErrorDialog (topWid, const_cast<char*>("Error"), args, 1)
: XmCreateWarningDialog (topWid, const_cast<char*>("Warning"), args, 1);
// remove text resource
XmStringFree (xmString);
// remove help and cancel buttons
XtUnmanageChild (XmMessageBoxGetChild (xMsgBox, XmDIALOG_CANCEL_BUTTON));
XtUnmanageChild (XmMessageBoxGetChild (xMsgBox, XmDIALOG_HELP_BUTTON));
// add callback to the "close" button that signals closing of the message box
XtAddCallback (xMsgBox, XmNokCallback, XmCloseMsgBox, NULL);
XtManageChild (xMsgBox);
XtRealizeWidget (topWid);
}
XtAppMainLoop (appShell);
XtUnrealizeWidget (topWid);
XtDestroyApplicationContext (appShell);
}
I would suggest that you look into one of the GUI libraries that support SDL as a backend. One such library would be GG, which has the class ThreeButtonDlg. When its Run() returns, you can look at its Result(). See the Initial method in their minimal example.