you can create dialog windows called "prompts" which have a message and some buttons...
authorDana Jansens <danakj@orodu.net>
Thu, 7 Feb 2008 02:35:55 +0000 (21:35 -0500)
committerDana Jansens <danakj@orodu.net>
Thu, 7 Feb 2008 07:14:43 +0000 (02:14 -0500)
Makefile.am
openbox/event.c
openbox/openbox.c
openbox/prompt.c [new file with mode: 0644]
openbox/prompt.h [new file with mode: 0644]
openbox/window.c
openbox/window.h
po/POTFILES.in
render/font.c
render/render.c
render/render.h

index 7eaa194..fc873bb 100644 (file)
@@ -256,6 +256,8 @@ openbox_openbox_SOURCES = \
        openbox/ping.h \
        openbox/place.c \
        openbox/place.h \
+       openbox/prompt.c \
+       openbox/prompt.h \
        openbox/popup.c \
        openbox/popup.h \
        openbox/resist.c \
index 7379749..4591009 100644 (file)
@@ -29,6 +29,7 @@
 #include "frame.h"
 #include "grab.h"
 #include "menu.h"
+#include "prompt.h"
 #include "menuframe.h"
 #include "keyboard.h"
 #include "mouse.h"
@@ -466,6 +467,7 @@ static void event_process(const XEvent *ec, gpointer data)
     ObDockApp *dockapp = NULL;
     ObWindow *obwin = NULL;
     ObMenuFrame *menu = NULL;
+    ObPrompt *prompt = NULL;
 
     /* make a copy we can mangle */
     ee = *ec;
@@ -488,6 +490,9 @@ static void event_process(const XEvent *ec, gpointer data)
         case OB_WINDOW_CLASS_INTERNAL:
             /* we don't do anything with events directly on these windows */
             break;
+        case OB_WINDOW_CLASS_PROMPT:
+            prompt = WINDOW_AS_PROMPT(obwin);
+            break;
         }
     }
     else
index 014199d..bef42ab 100644 (file)
@@ -41,6 +41,7 @@
 #include "group.h"
 #include "config.h"
 #include "ping.h"
+#include "prompt.h"
 #include "gettext.h"
 #include "render/render.h"
 #include "render/theme.h"
@@ -293,6 +294,7 @@ gint main(gint argc, gchar **argv)
             grab_startup(reconfigure);
             group_startup(reconfigure);
             ping_startup(reconfigure);
+            prompt_startup(reconfigure);
             client_startup(reconfigure);
             dock_startup(reconfigure);
             moveresize_startup(reconfigure);
@@ -349,6 +351,7 @@ gint main(gint argc, gchar **argv)
             moveresize_shutdown(reconfigure);
             dock_shutdown(reconfigure);
             client_shutdown(reconfigure);
+            prompt_shutdown(reconfigure);
             ping_shutdown(reconfigure);
             group_shutdown(reconfigure);
             grab_shutdown(reconfigure);
diff --git a/openbox/prompt.c b/openbox/prompt.c
new file mode 100644 (file)
index 0000000..d840092
--- /dev/null
@@ -0,0 +1,283 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   prompt.c for the Openbox window manager
+   Copyright (c) 2008        Dana Jansens
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "prompt.h"
+#include "openbox.h"
+#include "screen.h"
+#include "obt/display.h"
+#include "gettext.h"
+
+static GList *prompt_list = NULL;
+
+/* we construct these */
+static RrAppearance *prompt_a_button;
+static RrAppearance *prompt_a_hover;
+static RrAppearance *prompt_a_press;
+
+#define msg_appearance(self) (ob_rr_theme->osd_hilite_label)
+
+void prompt_startup(gboolean reconfig)
+{
+    RrColor *c_button, *c_hover, *c_press;
+
+    prompt_a_button = RrAppearanceCopy(ob_rr_theme->a_focused_unpressed_close);
+    prompt_a_hover = RrAppearanceCopy(ob_rr_theme->a_hover_focused_close);
+    prompt_a_press = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close);
+
+    c_button = prompt_a_button->texture[0].data.mask.color;
+    c_hover = prompt_a_button->texture[0].data.mask.color;
+    c_press = prompt_a_button->texture[0].data.mask.color;
+
+    RrAppearanceRemoveTextures(prompt_a_button);
+    RrAppearanceRemoveTextures(prompt_a_hover);
+    RrAppearanceRemoveTextures(prompt_a_press);
+
+    RrAppearanceAddTextures(prompt_a_button, 1);
+    RrAppearanceAddTextures(prompt_a_hover, 1);
+    RrAppearanceAddTextures(prompt_a_press, 1);
+
+    /* totally cheating here.. */
+    prompt_a_button->texture[0] = ob_rr_theme->osd_hilite_label->texture[0];
+    prompt_a_hover->texture[0] = ob_rr_theme->osd_hilite_label->texture[0];
+    prompt_a_press->texture[0] = ob_rr_theme->osd_hilite_label->texture[0];
+
+    prompt_a_button->texture[0].data.text.color = c_button;
+    prompt_a_hover->texture[0].data.text.color = c_hover;
+    prompt_a_press->texture[0].data.text.color = c_press;
+}
+
+void prompt_shutdown(gboolean reconfig)
+{
+    RrAppearanceFree(prompt_a_button);
+    RrAppearanceFree(prompt_a_hover);
+    RrAppearanceFree(prompt_a_press);
+}
+
+ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers)
+{
+    ObPrompt *self;
+    XSetWindowAttributes attrib;
+    guint i;
+    const gchar *const *c;
+
+    attrib.override_redirect = TRUE;
+
+    self = g_new0(ObPrompt, 1);
+    self->ref = 1;
+    self->super.type = OB_WINDOW_CLASS_PROMPT;
+    self->super.window = XCreateWindow(obt_display, obt_root(ob_screen),
+                                       0, 0, 1, 1, 0,
+                                       CopyFromParent, InputOutput,
+                                       CopyFromParent,
+                                       CWOverrideRedirect, &attrib);
+    window_add(&self->super.window, PROMPT_AS_WINDOW(self));
+
+    self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
+
+    self->msg.text = g_strdup(msg);
+    self->msg.window = XCreateWindow(obt_display, self->super.window,
+                                     0, 0, 1, 1, 0,
+                                     CopyFromParent, InputOutput,
+                                     CopyFromParent, 0, NULL);
+    XMapWindow(obt_display, self->msg.window);
+
+    self->n_buttons = 0;
+    for (c = answers; *c != NULL; ++c)
+        ++self->n_buttons;
+
+    if (!self->n_buttons)
+        self->n_buttons = 1;
+
+    self->button = g_new(ObPromptElement, self->n_buttons);
+
+    if (!answers) {
+        g_assert(self->n_buttons == 1); /* should be set to this above.. */
+        self->button[0].text = g_strdup(_("OK"));
+    }
+    else {
+        g_assert(self->n_buttons > 0);
+        for (i = 0; i < self->n_buttons; ++i)
+            self->button[i].text = g_strdup(answers[i]);
+    }
+
+    for (i = 0; i < self->n_buttons; ++i) {
+        self->button[i].window = XCreateWindow(obt_display, self->super.window,
+                                               0, 0, 1, 1, 0,
+                                               CopyFromParent, InputOutput,
+                                               CopyFromParent, 0, NULL);
+        XMapWindow(obt_display, self->button[i].window);
+        window_add(&self->button[i].window, PROMPT_AS_WINDOW(self));
+    }
+
+    return self;
+}
+
+void prompt_ref(ObPrompt *self)
+{
+    ++self->ref;
+}
+
+void prompt_unref(ObPrompt *self)
+{
+    if (self && --self->ref == 0) {
+        guint i;
+
+        for (i = 0; i < self->n_buttons; ++i) {
+            window_remove(self->button[i].window);
+            XDestroyWindow(obt_display, self->button[i].window);
+        }
+
+        XDestroyWindow(obt_display, self->msg.window);
+
+        RrAppearanceFree(self->a_bg);
+
+        window_remove(self->super.window);
+        XDestroyWindow(obt_display, self->super.window);
+        g_free(self);
+    }
+}
+
+static void prompt_layout(ObPrompt *self, const Rect *area)
+{
+    RrAppearance *a_msg = msg_appearance(self);
+    gint l, r, t, b;
+    guint i;
+    gint allbuttonsw, allbuttonsh, buttonx;
+    gint w, h;
+
+    const gint OUTSIDE_MARGIN = 4;
+    const gint MSG_BUTTON_SEPARATION = 4;
+    const gint BUTTON_SEPARATION = 4;
+
+    RrMargins(self->a_bg, &l, &t, &r, &b);
+    l += OUTSIDE_MARGIN;
+    t += OUTSIDE_MARGIN;
+    r += OUTSIDE_MARGIN;
+    b += OUTSIDE_MARGIN;
+
+    /* find the button sizes and how much space we need for them */
+    allbuttonsw = allbuttonsh = 0;
+    for (i = 0; i < self->n_buttons; ++i) {
+        gint bw, bh;
+
+        prompt_a_button->texture[0].data.text.string = self->button[i].text;
+        prompt_a_hover->texture[0].data.text.string = self->button[i].text;
+        prompt_a_press->texture[0].data.text.string = self->button[i].text;
+        RrMinSize(prompt_a_button, &bw, &bh);
+        self->button[i].width = bw;
+        self->button[i].height = bh;
+        RrMinSize(prompt_a_hover, &bw, &bh);
+        self->button[i].width = MAX(self->button[i].width, bw);
+        self->button[i].height = MAX(self->button[i].height, bh);
+        RrMinSize(prompt_a_press, &bw, &bh);
+        self->button[i].width = MAX(self->button[i].width, bw);
+        self->button[i].height = MAX(self->button[i].height, bh);
+
+        allbuttonsw += self->button[i].width + (i > 0 ? BUTTON_SEPARATION : 0);
+        allbuttonsh = MAX(allbuttonsh, self->button[i].height);
+    }
+
+    self->msg_wbound = MAX(allbuttonsw, area->width*3/5);
+
+    /* measure the text message area */
+    a_msg->texture[0].data.text.string = self->msg.text;
+    a_msg->texture[0].data.text.maxwidth = self->msg_wbound;
+    RrMinSize(a_msg, &self->msg.width, &self->msg.height);
+    a_msg->texture[0].data.text.maxwidth = 0;
+
+    /* width and height inside the outer margins */
+    w = MAX(self->msg.width, allbuttonsw);
+    h = self->msg.height + MSG_BUTTON_SEPARATION + allbuttonsh;
+
+    /* position the text message */
+    self->msg.x = l + (w - self->msg.width) / 2;
+    self->msg.y = t;
+
+    /* position the button buttons */
+    buttonx = l + (w - allbuttonsw) / 2;
+    for (i = 0; i < self->n_buttons; ++i) {
+        self->button[i].x = buttonx;
+        buttonx += self->button[i].width + BUTTON_SEPARATION;
+        self->button[i].y = h - allbuttonsh;
+        self->button[i].y += (allbuttonsh - self->button[i].height) / 2;
+    }
+
+    /* size and position the toplevel window */
+    self->width = w + l + r;
+    self->height = h + t + b;
+    self->x = (area->width - self->width) / 2;
+    self->y = (area->height - self->height) / 2;
+
+    /* move and resize the actual windows */
+    XMoveResizeWindow(obt_display, self->super.window,
+                      self->x, self->y, self->width, self->height);
+    XMoveResizeWindow(obt_display, self->msg.window,
+                      self->msg.x, self->msg.y,
+                      self->msg.width, self->msg.height);
+    for (i = 0; i < self->n_buttons; ++i)
+        XMoveResizeWindow(obt_display, self->button[i].window,
+                          self->button[i].x, self->button[i].y,
+                          self->button[i].width, self->button[i].height);
+}
+
+static void render_button(ObPrompt *self, ObPromptElement *e)
+{
+    prompt_a_button->surface.parent = self->a_bg;
+    prompt_a_button->surface.parentx = e->x;
+    prompt_a_button->surface.parentx = e->y;
+
+    prompt_a_button->texture[0].data.text.string = e->text;
+    RrPaint(prompt_a_button, e->window, e->width, e->height);
+}
+
+static void render_all(ObPrompt *self)
+{
+    guint i;
+
+    RrPaint(self->a_bg, self->super.window, self->width, self->height);
+
+    msg_appearance()->surface.parent = self->a_bg;
+    msg_appearance()->surface.parentx = self->msg.x;
+    msg_appearance()->surface.parentx = self->msg.y;
+
+    msg_appearance()->texture[0].data.text.string = self->msg.text;
+    msg_appearance()->texture[0].data.text.maxwidth = self->msg_wbound;
+    RrPaint(msg_appearance(), self->msg.window,
+            self->msg.width, self->msg.height);
+    msg_appearance()->texture[0].data.text.maxwidth = 0;
+
+    for (i = 0; i < self->n_buttons; ++i)
+        render_button(self, &self->button[i]);
+}
+
+void prompt_show(ObPrompt *self, const Rect *area)
+{
+    if (self->mapped) return;
+
+    prompt_layout(self, area);
+    render_all(self);
+    XMapWindow(obt_display, self->super.window);
+
+    self->mapped = TRUE;
+}
+
+void prompt_hide(ObPrompt *self)
+{
+    XUnmapWindow(obt_display, self->super.window);
+    self->mapped = FALSE;
+}
diff --git a/openbox/prompt.h b/openbox/prompt.h
new file mode 100644 (file)
index 0000000..deef3f2
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   prompt.h for the Openbox window manager
+   Copyright (c) 2008        Dana Jansens
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__prompt_h
+#define ob__prompt_h
+
+typedef struct _ObPrompt       ObPrompt;
+typedef struct _ObPromptElement ObPromptElement;
+
+#include "window.h"
+#include "geom.h"
+#include "render/render.h"
+#include <glib.h>
+
+struct _ObPromptElement {
+    gchar *text;
+    Window window;
+
+    gint x, y, width, height;
+};
+
+struct _ObPrompt
+{
+    ObInternalWindow super;
+    gint ref;
+
+    /* keep a copy of this because we re-render things that may need it
+       (i.e. the buttons) */
+    RrAppearance *a_bg;
+
+    gboolean mapped;
+    gint x, y, width, height;
+    gint msg_wbound;
+
+    ObPromptElement msg;
+
+    /* one for each answer */
+    ObPromptElement *button;
+    guint n_buttons;
+};
+
+void prompt_startup(gboolean reconfig);
+void prompt_shutdown(gboolean reconfig);
+
+ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers);
+void prompt_ref(ObPrompt *self);
+void prompt_unref(ObPrompt *self);
+
+/*! Show the prompt.  It will be centered within the given area rectangle */
+void prompt_show(ObPrompt *self, const Rect *area);
+void prompt_hide(ObPrompt *self);
+
+#endif
index 28b0857..88a727d 100644 (file)
@@ -23,6 +23,7 @@
 #include "client.h"
 #include "frame.h"
 #include "openbox.h"
+#include "prompt.h"
 #include "debug.h"
 #include "grab.h"
 
@@ -57,6 +58,8 @@ Window window_top(ObWindow *self)
         return WINDOW_AS_CLIENT(self)->frame->window;
     case OB_WINDOW_CLASS_INTERNAL:
         return WINDOW_AS_INTERNAL(self)->window;
+    case OB_WINDOW_CLASS_PROMPT:
+        return WINDOW_AS_PROMPT(self)->super.window;
     }
     g_assert_not_reached();
     return None;
@@ -71,6 +74,7 @@ ObStackingLayer window_layer(ObWindow *self)
         return ((ObClient*)self)->layer;
     case OB_WINDOW_CLASS_MENUFRAME:
     case OB_WINDOW_CLASS_INTERNAL:
+    case OB_WINDOW_CLASS_PROMPT:
         return OB_STACKING_LAYER_INTERNAL;
     }
     g_assert_not_reached();
index cc380d3..24a7d07 100644 (file)
@@ -31,7 +31,8 @@ typedef enum {
     OB_WINDOW_CLASS_MENUFRAME,
     OB_WINDOW_CLASS_DOCK,
     OB_WINDOW_CLASS_CLIENT,
-    OB_WINDOW_CLASS_INTERNAL
+    OB_WINDOW_CLASS_INTERNAL,
+    OB_WINDOW_CLASS_PROMPT
 } ObWindowClass;
 
 /* In order to be an ObWindow, you need to make this struct the top of your
@@ -48,21 +49,26 @@ struct _ObWindow {
     (((ObWindow*)win)->type == OB_WINDOW_CLASS_CLIENT)
 #define WINDOW_IS_INTERNAL(win) \
     (((ObWindow*)win)->type == OB_WINDOW_CLASS_INTERNAL)
+#define WINDOW_IS_PROMPT(win) \
+    (((ObWindow*)win)->type == OB_WINDOW_CLASS_PROMPT)
 
 struct _ObMenu;
 struct _ObDock;
 struct _ObDockApp;
 struct _ObClient;
+struct _ObPrompt;
 
 #define WINDOW_AS_MENUFRAME(win) ((struct _ObMenuFrame*)win)
 #define WINDOW_AS_DOCK(win) ((struct _ObDock*)win)
 #define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win)
 #define WINDOW_AS_INTERNAL(win) ((struct _ObInternalWindow*)win)
+#define WINDOW_AS_PROMPT(win) ((struct _ObPrompt*)win)
 
 #define MENUFRAME_AS_WINDOW(menu) ((ObWindow*)menu)
 #define DOCK_AS_WINDOW(dock) ((ObWindow*)dock)
 #define CLIENT_AS_WINDOW(client) ((ObWindow*)client)
 #define INTERNAL_AS_WINDOW(intern) ((ObWindow*)intern)
+#define PROMPT_AS_WINDOW(prompt) ((ObWindow*)prompt)
 
 void window_startup (gboolean reconfig);
 void window_shutdown(gboolean reconfig);
index 00f0dcf..6de409f 100644 (file)
@@ -15,3 +15,4 @@ openbox/screen.c
 openbox/session.c
 openbox/startupnotify.c
 openbox/translate.c
+openbox/prompt.c
index 356b9c6..205eab5 100644 (file)
@@ -109,6 +109,7 @@ RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
     /* setup the layout */
     pango_layout_set_font_description(out->layout, out->font_desc);
     pango_layout_set_single_paragraph_mode(out->layout, TRUE);
+    pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR);
 
     /* get the ascent and descent */
     measure_font(inst, out);
@@ -139,12 +140,14 @@ void RrFontClose(RrFont *f)
 }
 
 static void font_measure_full(const RrFont *f, const gchar *str,
-                              gint *x, gint *y, gint shadow_x, gint shadow_y)
+                              gint *x, gint *y, gint shadow_x, gint shadow_y,
+                              gint maxwidth)
 {
     PangoRectangle rect;
 
     pango_layout_set_text(f->layout, str, -1);
-    pango_layout_set_width(f->layout, -1);
+    pango_layout_set_width(f->layout,
+                           (maxwidth <= 0 ? -1 : maxwidth * PANGO_SCALE));
 
     /* pango_layout_get_pixel_extents lies! this is the right way to get the
        size of the text's area */
@@ -163,11 +166,12 @@ static void font_measure_full(const RrFont *f, const gchar *str,
 }
 
 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
-                            gint shadow_x, gint shadow_y)
+                            gint shadow_x, gint shadow_y, gint maxwidth)
 {
     RrSize *size;
     size = g_new(RrSize, 1);
-    font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y);
+    font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y,
+                      maxwidth);
     return size;
 }
 
index 4119dc7..2147df0 100644 (file)
@@ -178,6 +178,14 @@ RrAppearance *RrAppearanceNew(const RrInstance *inst, gint numtex)
   return out;
 }
 
+void RrAppearanceRemoveTextures(RrAppearance *a)
+{
+    gint i;
+
+    g_free(a->texture);
+    a->textures = 0;
+}
+
 void RrAppearanceAddTextures(RrAppearance *a, gint numtex)
 {
     g_assert(a->textures == 0);
@@ -378,7 +386,8 @@ gint RrMinWidth(RrAppearance *a)
             m = RrFontMeasureString(a->texture[i].data.text.font,
                                     a->texture[i].data.text.string,
                                     a->texture[i].data.text.shadow_offset_x,
-                                    a->texture[i].data.text.shadow_offset_y);
+                                    a->texture[i].data.text.shadow_offset_y,
+                                    a->texture[i].data.text.maxwidth);
             w = MAX(w, m->width);
             g_free(m);
             break;
index e4ca914..b83a53d 100644 (file)
@@ -141,6 +141,7 @@ struct _RrTextureText {
     gboolean shortcut; /*!< Underline a character */
     guint shortcut_pos; /*!< Position in bytes of the character to underline */
     RrEllipsizeMode ellipsize;
+    gint maxwidth;
 };
 
 struct _RrPixmapMask {
@@ -244,6 +245,7 @@ GC       RrColorGC    (RrColor *c);
 RrAppearance *RrAppearanceNew  (const RrInstance *inst, gint numtex);
 RrAppearance *RrAppearanceCopy (RrAppearance *a);
 void          RrAppearanceFree (RrAppearance *a);
+void          RrAppearanceRemoveTextures(RrAppearance *a);
 void          RrAppearanceAddTextures(RrAppearance *a, gint numtex);
 
 RrFont *RrFontOpen          (const RrInstance *inst, const gchar *name,
@@ -251,7 +253,8 @@ RrFont *RrFontOpen          (const RrInstance *inst, const gchar *name,
 RrFont *RrFontOpenDefault   (const RrInstance *inst);
 void    RrFontClose         (RrFont *f);
 RrSize *RrFontMeasureString (const RrFont *f, const gchar *str,
-                             gint shadow_offset_x, gint shadow_offset_y);
+                             gint shadow_offset_x, gint shadow_offset_y,
+                             gint maxwidth);
 gint    RrFontHeight        (const RrFont *f, gint shadow_offset_y);
 gint    RrFontMaxCharWidth  (const RrFont *f);