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

帅比萌擦擦* 提交于 2019-11-30 09:29:10
    6

    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)
    
      3

      I personally use Qt4's QMessageBox.

      example:

      QMessageBox mb(QMessageBox::Question, "Title", "Message",  QMessageBox::Ok | QMessageBox::Cancel);
      if(mb.exec() == QMessageBox::Ok) { do_stuff(); }
      
      • Thanks. What is the name of the Qt4 library please (-l<libname>)? – karx11erx Sep 5 '09 at 20:12
      • 1
        @karx11erx: Qt is more than just a small library, it is an entire cross platform GUI (and more) solution. Using it requires more than just linking to a particular library. Often it is best to use their build system overall. Qt is often an "all or nothing" choice. – Evan Teran Sep 5 '09 at 20:29
      • 1
        Using Qt4 makes gcc 4.4.0 throw a lot of errors for me, and I don't need a behemoth on top of my app. – karx11erx Sep 5 '09 at 20:37
      • well most any GUI toolkit is going to be a "behemoth" compare to not using one (think about how large Win32 is!). The errors are almost certainly due to not correctly using the Qt build system. – Evan Teran Sep 5 '09 at 20:41
      2

      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.

      • SDL is obviously creating a top level window for OpenGL rendering - I mentioned that in my initial question. There is no way to obtain its handle except modifying SDL though. – karx11erx Sep 5 '09 at 20:16
      • Actually all I need is to make X(11) display such a window for me, but from what I've found on the inet on this programming any stuff in X is non-trivial. – karx11erx Sep 5 '09 at 20:42
      • Would it be possible to create a Motif Widget from the X11 window handle SDL has? – karx11erx Sep 5 '09 at 21:08
      • It would seem so, but I really don't know. Did you try just passing your X11 window handle to the Message Box function? Seems like that would work. – Matthew Talbert Sep 6 '09 at 0:16
      • How can I kill XtAppMainLoop from inside the program, or make it terminate after the user has clicked on my message box's ok button? – karx11erx Sep 6 '09 at 10:49
      2

      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);
      }
      
        1

        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.

        • Thanks, but too complicated. – karx11erx Sep 5 '09 at 20:39

        Your Answer

        By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

        Not the answer you're looking for? Browse other questions tagged or ask your own question.

        易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
        该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!