mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
Initial commit of notification daemon
This commit is contained in:
parent
dd496f4780
commit
9635713236
18
ede-notify-daemon/Jamfile
Normal file
18
ede-notify-daemon/Jamfile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# $Id: Jamfile 2331 2008-09-22 12:49:55Z karijes $
|
||||||
|
#
|
||||||
|
# Part of Equinox Desktop Environment (EDE).
|
||||||
|
# Copyright (c) 2012 EDE Authors.
|
||||||
|
#
|
||||||
|
# This program is licensed under terms of the
|
||||||
|
# GNU General Public License version 2 or newer.
|
||||||
|
# See COPYING for details.
|
||||||
|
|
||||||
|
SubDir TOP ede-notify-daemon ;
|
||||||
|
|
||||||
|
SOURCE = ede-notify-daemon.cpp NotifyWindow.cpp ;
|
||||||
|
|
||||||
|
EdeProgram ede-notify-daemon : $(SOURCE) ;
|
||||||
|
LinkAgainst ede-notify-daemon : $(EDELIB_DBUS_LIB) ;
|
||||||
|
|
||||||
|
TranslationStrings locale : $(SOURCE) ;
|
87
ede-notify-daemon/NotifyWindow.cpp
Normal file
87
ede-notify-daemon/NotifyWindow.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <FL/Fl.H>
|
||||||
|
#include <FL/fl_draw.H>
|
||||||
|
#include <edelib/Debug.h>
|
||||||
|
#include <edelib/IconLoader.h>
|
||||||
|
#include <edelib/Nls.h>
|
||||||
|
#include "NotifyWindow.h"
|
||||||
|
|
||||||
|
/* default sizes for window */
|
||||||
|
#define DEFAULT_W 280
|
||||||
|
#define DEFAULT_H 75
|
||||||
|
#define DEFAULT_EXPIRE 2000
|
||||||
|
|
||||||
|
EDELIB_NS_USING(IconLoader)
|
||||||
|
EDELIB_NS_USING(ICON_SIZE_MEDIUM)
|
||||||
|
|
||||||
|
extern int FL_NORMAL_SIZE;
|
||||||
|
|
||||||
|
static void close_cb(Fl_Widget*, void *w) {
|
||||||
|
NotifyWindow *win = (NotifyWindow*)w;
|
||||||
|
win->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timeout_cb(void *w) {
|
||||||
|
close_cb(0, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotifyWindow::NotifyWindow() : Fl_Window(DEFAULT_W, DEFAULT_H) {
|
||||||
|
FL_NORMAL_SIZE = 12;
|
||||||
|
|
||||||
|
type(NOTIFYWINDOW_TYPE);
|
||||||
|
color(FL_BACKGROUND2_COLOR);
|
||||||
|
box(FL_THIN_UP_BOX);
|
||||||
|
begin();
|
||||||
|
closeb = new Fl_Button(255, 10, 20, 20, "x");
|
||||||
|
closeb->box(FL_NO_BOX);
|
||||||
|
closeb->color(FL_BACKGROUND2_COLOR);
|
||||||
|
closeb->labelsize(12);
|
||||||
|
closeb->tooltip(_("Close this notification"));
|
||||||
|
closeb->callback(close_cb, this);
|
||||||
|
|
||||||
|
imgbox = new Fl_Box(10, 10, 48, 48);
|
||||||
|
imgbox->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
|
||||||
|
|
||||||
|
summary = new Fl_Output(65, 10, 185, 25);
|
||||||
|
/* use flat box so text can be drawn correctly */
|
||||||
|
summary->box(FL_FLAT_BOX);
|
||||||
|
summary->cursor_color(FL_BACKGROUND2_COLOR);
|
||||||
|
|
||||||
|
body = new Fl_Output(65, 31, 185, 25);
|
||||||
|
/* use flat box so text can be drawn correctly */
|
||||||
|
body->box(FL_FLAT_BOX);
|
||||||
|
body->cursor_color(FL_BACKGROUND2_COLOR);
|
||||||
|
end();
|
||||||
|
border(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyWindow::set_icon(const char *img) {
|
||||||
|
E_RETURN_IF_FAIL(IconLoader::inited());
|
||||||
|
E_RETURN_IF_FAIL(img != NULL);
|
||||||
|
|
||||||
|
IconLoader::set(imgbox, img, ICON_SIZE_MEDIUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyWindow::set_summary(const char *s) {
|
||||||
|
E_ASSERT(s != NULL && "Got NULL string?");
|
||||||
|
summary->value(s);
|
||||||
|
|
||||||
|
int W = 0, H = 0;
|
||||||
|
fl_measure(summary->value(), W, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyWindow::set_body(const char *s) {
|
||||||
|
E_ASSERT(s != NULL && "Got NULL string?");
|
||||||
|
body->value(s);
|
||||||
|
|
||||||
|
int W = 0, H = 0;
|
||||||
|
fl_measure(summary->value(), W, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyWindow::show(void) {
|
||||||
|
if(exp != 0) {
|
||||||
|
if(exp == -1) exp = DEFAULT_EXPIRE;
|
||||||
|
Fl::add_timeout((double)exp / (double)1000, timeout_cb, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fl_Window::show();
|
||||||
|
}
|
39
ede-notify-daemon/NotifyWindow.h
Normal file
39
ede-notify-daemon/NotifyWindow.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef __NOTIFYWINDOW_H__
|
||||||
|
#define __NOTIFYWINDOW_H__
|
||||||
|
|
||||||
|
#include <FL/Fl_Window.H>
|
||||||
|
#include <FL/Fl_Button.H>
|
||||||
|
#include <FL/Fl_Box.H>
|
||||||
|
#include <FL/Fl_Output.H>
|
||||||
|
|
||||||
|
/* just keep it greater than FL_WINDOW or FL_DOUBLE_WINDOW */
|
||||||
|
#define NOTIFYWINDOW_TYPE 0xF9
|
||||||
|
|
||||||
|
class NotifyWindow : public Fl_Window {
|
||||||
|
private:
|
||||||
|
int id;
|
||||||
|
int exp;
|
||||||
|
Fl_Button *closeb;
|
||||||
|
Fl_Box *imgbox;
|
||||||
|
Fl_Output *summary;
|
||||||
|
Fl_Output *body;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NotifyWindow();
|
||||||
|
|
||||||
|
void set_id(int i) { id = i; }
|
||||||
|
int get_id(void) { return id; }
|
||||||
|
|
||||||
|
void set_icon(const char *img);
|
||||||
|
void set_summary(const char *s);
|
||||||
|
void set_body(const char *s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* match to spec: if is -1, then we handle it, if is 0, then window will not be closed and
|
||||||
|
* the rest is sender specific
|
||||||
|
*/
|
||||||
|
void set_expire(int t) { exp = t; }
|
||||||
|
void show(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
311
ede-notify-daemon/ede-notify-daemon.cpp
Normal file
311
ede-notify-daemon/ede-notify-daemon.cpp
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <FL/Fl.H>
|
||||||
|
#include <FL/Fl_Shared_Image.H>
|
||||||
|
#include <edelib/Debug.h>
|
||||||
|
#include <edelib/EdbusConnection.h>
|
||||||
|
#include <edelib/EdbusMessage.h>
|
||||||
|
#include <edelib/EdbusData.h>
|
||||||
|
#include <edelib/EdbusList.h>
|
||||||
|
#include <edelib/EdbusDict.h>
|
||||||
|
#include <edelib/IconLoader.h>
|
||||||
|
#include <edelib/Netwm.h>
|
||||||
|
|
||||||
|
#include "NotifyWindow.h"
|
||||||
|
|
||||||
|
/* server info sent via GetServerInformation */
|
||||||
|
#define EDE_NOTIFY_DAEMON_NAME "EDE Notification Daemon"
|
||||||
|
#define EDE_NOTIFY_DAEMON_VENDOR "ede"
|
||||||
|
#define EDE_NOTIFY_DAEMON_VERSION "0.1"
|
||||||
|
#define EDE_NOTIFY_DAEMON_SPEC_VERSION "1.2"
|
||||||
|
|
||||||
|
#define NOTIFICATIONS_DBUS_PATH "/org/freedesktop/Notifications"
|
||||||
|
#define NOTIFICATIONS_DBUS_SERVICE_NAME "org.freedesktop.Notifications"
|
||||||
|
|
||||||
|
/* types of urgency levels */
|
||||||
|
#define URGENCY_LOW 0
|
||||||
|
#define URGENCY_NORMAL 1
|
||||||
|
#define URGENCY_CRITICAL 2
|
||||||
|
|
||||||
|
/* from FLTK */
|
||||||
|
#define FOREVER 1e20
|
||||||
|
|
||||||
|
/* space between shown windows (on both side) */
|
||||||
|
#define WINDOWS_PADDING 10
|
||||||
|
|
||||||
|
#define IS_MEMBER(m, s1) (strcmp((m->member()), (s1)) == 0)
|
||||||
|
|
||||||
|
EDELIB_NS_USING(EdbusConnection)
|
||||||
|
EDELIB_NS_USING(EdbusMessage)
|
||||||
|
EDELIB_NS_USING(EdbusData)
|
||||||
|
EDELIB_NS_USING(EdbusList)
|
||||||
|
EDELIB_NS_USING(EdbusDict)
|
||||||
|
EDELIB_NS_USING(EdbusVariant)
|
||||||
|
EDELIB_NS_USING(IconLoader)
|
||||||
|
EDELIB_NS_USING(netwm_workarea_get_size)
|
||||||
|
EDELIB_NS_USING(EDBUS_SESSION)
|
||||||
|
EDELIB_NS_USING(EDBUS_TYPE_INVALID)
|
||||||
|
|
||||||
|
static bool server_running;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list of server capabilities
|
||||||
|
* check all available on: http://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html
|
||||||
|
*/
|
||||||
|
static const char *server_caps[] = {"actions", "body", "icon-static", 0};
|
||||||
|
|
||||||
|
/* increased every time new notification window is shown; must be less than UINT_MAX */
|
||||||
|
static unsigned int notify_id;
|
||||||
|
|
||||||
|
static bool empty_str(const char *s) {
|
||||||
|
if(!s || !strlen(s))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte_t get_urgency_level(EdbusDict &hints) {
|
||||||
|
EdbusData ret = hints.find(EdbusData::from_string("urgency"));
|
||||||
|
/* default */
|
||||||
|
E_RETURN_VAL_IF_FAIL(ret.type() != EDBUS_TYPE_INVALID, URGENCY_NORMAL);
|
||||||
|
|
||||||
|
/* FIXME: spec said this is byte by I'm getting variant here ??? */
|
||||||
|
E_RETURN_VAL_IF_FAIL(ret.is_variant(), URGENCY_NORMAL);
|
||||||
|
|
||||||
|
EdbusVariant v = ret.to_variant();
|
||||||
|
/* must be byte */
|
||||||
|
E_RETURN_VAL_IF_FAIL(v.value.is_byte(), URGENCY_NORMAL);
|
||||||
|
return v.value.to_byte();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_int_coordinate(const char *n, EdbusDict &hints, int &c) {
|
||||||
|
EdbusData ret = hints.find(EdbusData::from_string(n));
|
||||||
|
|
||||||
|
/* it is fine to be not present */
|
||||||
|
if(ret.type() == EDBUS_TYPE_INVALID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* coordinates are 'int'; but which fucking 'int'; int32, int16, int64 ??? */
|
||||||
|
if(ret.is_int32())
|
||||||
|
c = (int)ret.to_int32();
|
||||||
|
else if(ret.is_int16())
|
||||||
|
c = (int)ret.to_int16();
|
||||||
|
else if(ret.is_int64())
|
||||||
|
c = (int)ret.to_int64();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_window(unsigned int id,
|
||||||
|
const char *app_name,
|
||||||
|
const char *app_icon,
|
||||||
|
const char *summary,
|
||||||
|
const char *body,
|
||||||
|
int expire_timeout,
|
||||||
|
EdbusDict &hints)
|
||||||
|
{
|
||||||
|
byte_t u = get_urgency_level(hints);
|
||||||
|
|
||||||
|
NotifyWindow *win = new NotifyWindow();
|
||||||
|
|
||||||
|
if(!empty_str(summary))
|
||||||
|
win->set_summary(summary);
|
||||||
|
if(!empty_str(body))
|
||||||
|
win->set_body(body);
|
||||||
|
if(empty_str(app_icon)) {
|
||||||
|
switch(u) {
|
||||||
|
case URGENCY_CRITICAL:
|
||||||
|
app_icon = "dialog-error";
|
||||||
|
break;
|
||||||
|
case URGENCY_LOW:
|
||||||
|
case URGENCY_NORMAL:
|
||||||
|
default:
|
||||||
|
app_icon = "dialog-information";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
win->set_icon(app_icon);
|
||||||
|
win->set_id(id);
|
||||||
|
win->set_expire(expire_timeout);
|
||||||
|
|
||||||
|
/* according to spec, both coordinates must exist so window can be positioned as desired */
|
||||||
|
int X, Y;
|
||||||
|
if(get_int_coordinate("x", hints, X) &&
|
||||||
|
get_int_coordinate("y", hints, Y))
|
||||||
|
{
|
||||||
|
win->position(X, Y);
|
||||||
|
} else {
|
||||||
|
int sx, sy, sw, sh;
|
||||||
|
if(!netwm_workarea_get_size(sx, sy, sw, sh))
|
||||||
|
Fl::screen_xywh(sx, sy, sw, sh);
|
||||||
|
|
||||||
|
/* default positions */
|
||||||
|
int px, py;
|
||||||
|
px = sw - WINDOWS_PADDING - win->w();
|
||||||
|
py = sh - WINDOWS_PADDING - win->h();
|
||||||
|
|
||||||
|
Fl::lock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iterate through shown windows and find position where to put our one
|
||||||
|
* FIXME: this is quite primitive window position deducing facility
|
||||||
|
*/
|
||||||
|
Fl_Window *wi;
|
||||||
|
for(wi = Fl::first_window(); wi; wi = Fl::next_window(wi)) {
|
||||||
|
if(wi->type() != NOTIFYWINDOW_TYPE) continue;
|
||||||
|
|
||||||
|
py -= wi->h() + WINDOWS_PADDING;
|
||||||
|
|
||||||
|
if(py < sy) {
|
||||||
|
px -= wi->w() + WINDOWS_PADDING;
|
||||||
|
py = sh - WINDOWS_PADDING - wi->h();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Fl::unlock();
|
||||||
|
win->position(px, py);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we are already running loop, so window will handle events */
|
||||||
|
win->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_notify(EdbusConnection *dbus, const EdbusMessage *m) {
|
||||||
|
if(m->size() != 8) {
|
||||||
|
E_WARNING(E_STRLOC ": Received malformed 'Notify'. Ignoring...\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *app_name, *app_icon, *summary, *body;
|
||||||
|
app_name = app_icon = summary = body = NULL;
|
||||||
|
unsigned int replaces_id;
|
||||||
|
int expire_timeout;
|
||||||
|
|
||||||
|
EdbusMessage::const_iterator it = m->begin();
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_string(), 0);
|
||||||
|
|
||||||
|
app_name = it->to_string();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_uint32(), 0);
|
||||||
|
replaces_id = it->to_uint32();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_string(), 0);
|
||||||
|
app_icon = it->to_string();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_string(), 0);
|
||||||
|
summary = it->to_string();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_string(), 0);
|
||||||
|
body = it->to_string();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_array(), 0);
|
||||||
|
EdbusList array = it->to_array();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_dict(), 0);
|
||||||
|
/* we are supporting only 'urgency' and x/y hints here */
|
||||||
|
EdbusDict hints = it->to_dict();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
E_RETURN_VAL_IF_FAIL(it->is_int32(), 0);
|
||||||
|
expire_timeout = it->to_int32();
|
||||||
|
|
||||||
|
/* specification dumb stuff: what if we got UINT_MAX?? here we will reverse to first ID */
|
||||||
|
if(++notify_id == UINT_MAX) notify_id = 1;
|
||||||
|
|
||||||
|
if(replaces_id) {
|
||||||
|
//replaces_id == notify_id;
|
||||||
|
} else {
|
||||||
|
show_window(notify_id, app_name, app_icon, summary, body, expire_timeout, hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reply sent to client */
|
||||||
|
EdbusMessage reply;
|
||||||
|
reply.create_reply(*m);
|
||||||
|
reply << EdbusData::from_uint32(replaces_id);
|
||||||
|
dbus->send(reply);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int notifications_dbus_method_cb(const EdbusMessage *m, void *d) {
|
||||||
|
EdbusConnection *dbus = (EdbusConnection*)d;
|
||||||
|
|
||||||
|
/* void org.freedesktop.Notifications.GetServerInformation (out STRING name, out STRING vendor, out STRING version) */
|
||||||
|
if(IS_MEMBER(m, "GetServerInformation")) {
|
||||||
|
EdbusMessage reply;
|
||||||
|
reply.create_reply(*m);
|
||||||
|
reply << EdbusData::from_string(EDE_NOTIFY_DAEMON_NAME)
|
||||||
|
<< EdbusData::from_string(EDE_NOTIFY_DAEMON_VENDOR)
|
||||||
|
<< EdbusData::from_string(EDE_NOTIFY_DAEMON_VERSION)
|
||||||
|
<< EdbusData::from_string(EDE_NOTIFY_DAEMON_SPEC_VERSION); /* without this notify-send will not work */
|
||||||
|
|
||||||
|
dbus->send(reply);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STRING_ARRAY org.freedesktop.Notifications.GetCapabilities (void) */
|
||||||
|
if(IS_MEMBER(m, "GetCapabilities")) {
|
||||||
|
EdbusList array = EdbusList::create_array();
|
||||||
|
for(int i = 0; server_caps[i]; i++)
|
||||||
|
array << EdbusData::from_string(server_caps[i]);
|
||||||
|
|
||||||
|
EdbusMessage reply;
|
||||||
|
reply.create_reply(*m);
|
||||||
|
reply << array;
|
||||||
|
dbus->send(reply);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UINT32 org.freedesktop.Notifications.Notify (STRING app_name, UINT32 replaces_id, STRING app_icon, STRING summary, STRING body, ARRAY actions, DICT hints, INT32 expire_timeout) */
|
||||||
|
if(IS_MEMBER(m, "Notify"))
|
||||||
|
return handle_notify(dbus, m);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int notifications_dbus_signal_cb(const EdbusMessage *m, void *d) {
|
||||||
|
E_DEBUG("+=> %s\n", m->member());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
server_running = false;
|
||||||
|
notify_id = 0;
|
||||||
|
EdbusConnection dbus;
|
||||||
|
|
||||||
|
IconLoader::init();
|
||||||
|
fl_register_images();
|
||||||
|
fl_open_display();
|
||||||
|
|
||||||
|
if(!dbus.connect(EDBUS_SESSION)) {
|
||||||
|
E_WARNING(E_STRLOC ": Unable to connect to session bus. Is your dbus daemon running?");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!dbus.request_name(NOTIFICATIONS_DBUS_SERVICE_NAME)) {
|
||||||
|
E_WARNING(E_STRLOC ": Seems notification daemon is already running. Quitting...\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus.register_object(NOTIFICATIONS_DBUS_PATH);
|
||||||
|
dbus.method_callback(notifications_dbus_method_cb, &dbus);
|
||||||
|
//dbus.signal_callback(notifications_dbus_signal_cb, &dbus);
|
||||||
|
dbus.setup_listener_with_fltk();
|
||||||
|
server_running = true;
|
||||||
|
|
||||||
|
while(server_running)
|
||||||
|
Fl::wait(FOREVER);
|
||||||
|
|
||||||
|
IconLoader::shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
10
ede-notify-daemon/test-notifications.sh
Executable file
10
ede-notify-daemon/test-notifications.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# simple script that will fire set of events so daemon window positioning (and other) can be tested
|
||||||
|
|
||||||
|
N_EVENTS=5
|
||||||
|
|
||||||
|
notify_cmd="notify-send --expire-time=10000"
|
||||||
|
|
||||||
|
for((i=0;i<$N_EVENTS;i++)) do
|
||||||
|
$notify_cmd "title $i" "this is content"
|
||||||
|
done
|
24
ede-notify-daemon/window.fl
Normal file
24
ede-notify-daemon/window.fl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# data file for the Fltk User Interface Designer (fluid)
|
||||||
|
version 1.0300
|
||||||
|
header_name {.h}
|
||||||
|
code_name {.cxx}
|
||||||
|
Function {} {open
|
||||||
|
} {
|
||||||
|
Fl_Window {} {open selected
|
||||||
|
xywh {1219 684 280 75} type Double color 7 visible
|
||||||
|
} {
|
||||||
|
Fl_Button {} {
|
||||||
|
label x
|
||||||
|
xywh {255 10 20 20} box NO_BOX color 7 labelsize 12
|
||||||
|
}
|
||||||
|
Fl_Box {} {
|
||||||
|
image {../../../../.icons/edeneu/32x32/status/dialog-information.png} xywh {10 10 48 48} align 20
|
||||||
|
}
|
||||||
|
Fl_Output {} {
|
||||||
|
xywh {65 10 185 24} box NO_BOX labelsize 12
|
||||||
|
}
|
||||||
|
Fl_Output {} {
|
||||||
|
xywh {65 31 185 24} box NO_BOX labelsize 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user