A fast MQTT dashboard application and rule engine framework written in C for Linux, Raspberry Pi and WINDOWS.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

434 lines
15 KiB

/* MENU.C (c) Markus Hoffmann */
/* This file is part of MQTT-Hyperdash, the MQTT Dashboard
* ============================================================
* MQTT-Hyperdash is free software and comes with NO WARRANTY - read the file
* COPYING for details
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fnmatch.h>
#include <gtk/gtk.h>
#include "config.h"
#include "basics.h"
#include "graphics.h"
#include "hyperdash.h"
#include "file.h"
#include "input.h"
#include "dashdesign.h"
#include "util.h"
#include "menu.h"
#include "element_groups.h"
static MENUENTRY menuentries[];
/* Initializes the menu */
GtkWidget *init_menu() {
GtkWidget *menu;
GtkWidget *menu_bar;
int i=0;
/* Init the menu-widget, and remember -- never
* gtk_show_widget() the menu widget!!
* This is the menu that holds the menu items, the one that
* will pop up when you click on the "Root Menu" in the app */
menu=gtk_menu_new();
/* Create a menu-bar to hold the menus for add it to main window */
menu_bar=gtk_menu_bar_new();
/* Next we make a little loop that makes menu-entries.
* Notice the call to gtk_menu_shell_append. Here we are adding a list of
* menu items to our menu. */
while(menuentries[i].text) {
if(menuentries[i].typ==MENU_TITLE) {
/* Create a new menu-item with a name... */
menuentries[i].widget=gtk_menu_item_new_with_label(menuentries[i].text);
/* Show the widget */
gtk_widget_show (menuentries[i].widget);
/* Now we specify that we want our newly created "menu" to be the menu
* for the "root menu" */
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuentries[i].widget), menu);
/* Init the menu-widget, and remember -- never
* gtk_show_widget() the menu widget!!
* This is the menu that holds the menu items, the one that
* will pop up when you click on the "Root Menu" in the app */
menu = gtk_menu_new ();
/* And finally we append the menu-item to the menu-bar -- this is the
* "root" menu-item I have been raving about =) */
gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar),menuentries[i].widget );
} else {
/* Create a new menu-item with a name... */
if(*menuentries[i].text=='-') menuentries[i].widget=gtk_separator_menu_item_new();
else if(*menuentries[i].text=='[') menuentries[i].widget=gtk_check_menu_item_new_with_label(menuentries[i].text+4);
else if(*menuentries[i].text=='#') {
menuentries[i].widget=gtk_separator_menu_item_new();
int k;
GtkWidget *gw;
GSList *group = NULL;
for(k=1;k<anzeltyp;k++) {
if(strcmp(eltyps[k].name,"BROKER") && strcmp(eltyps[k].name,"PANEL")) {
gw=gtk_radio_menu_item_new_with_label(group,eltyps[k].name);
group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (gw));
if(k==DEFAULT_ELEMENT) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gw),TRUE);
gtk_menu_shell_append(GTK_MENU_SHELL (menu),gw);
g_signal_connect_swapped(gw, "activate",
G_CALLBACK (menuentries[i].function),
(void *) eltyps[k].name);
gtk_widget_show (gw);
}
}
}
else if(*menuentries[i].text=='%') {
menuentries[i].widget=gtk_separator_menu_item_new();
int k;
GtkWidget *gw;
GSList *group = NULL;
for(k=0;k<anzgroups;k++) {
gw=gtk_radio_menu_item_new_with_label(group,groups[k].name);
group=gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM (gw));
if(k==DEFAULT_GROUP) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gw),TRUE);
gtk_menu_shell_append(GTK_MENU_SHELL (menu),gw);
g_signal_connect_swapped(gw, "activate",
G_CALLBACK (menuentries[i].function),
(void *) groups[k].name);
gtk_widget_show (gw);
}
}
else menuentries[i].widget=gtk_menu_item_new_with_label(menuentries[i].text);
/* ...and add it to the menu. */
gtk_menu_shell_append (GTK_MENU_SHELL (menu),menuentries[i].widget);
/* Do something interesting when the menuitem is selected */
g_signal_connect_swapped(menuentries[i].widget, "activate",
G_CALLBACK (menuentries[i].function),
(MENUENTRY *) &menuentries[i]);
/* Show the widget */
gtk_widget_show(menuentries[i].widget);
}
i++;
}
return(menu_bar);
}
GdkCursor *cursor=NULL;
/* Set the mouse cursor which will be used when the mouse enters the
drawing area.*/
static void set_cursor(const char *name) {
if(!strcmp(name,"delete")) cursor=gdk_cursor_new(GDK_PIRATE);
else if(!strcmp(name,"add")) cursor=gdk_cursor_new(GDK_PLUS);
else if(!strcmp(name,"edit")) cursor=gdk_cursor_new(GDK_PENCIL);
else if(!strcmp(name,"size")) cursor=gdk_cursor_new(GDK_SIZING);
else if(!strcmp(name,"color")) cursor=gdk_cursor_new(GDK_SPRAYCAN);
else if(!strcmp(name,"?")) cursor=gdk_cursor_new(GDK_QUESTION_ARROW);
else cursor=gdk_cursor_new_from_name(gdk_display_get_default(),name);
}
static void menu_move(MENUENTRY *me) {current_action=A_MOVE; set_cursor("move"); update_statusline();}
static void menu_copy(MENUENTRY *me) {current_action=A_COPY; set_cursor("copy"); update_statusline();}
static void menu_identify(MENUENTRY *me) {current_action=A_NONE; set_cursor("?"); update_statusline();}
static void menu_resize(MENUENTRY *me) {current_action=A_RESIZE; set_cursor("size"); update_statusline();}
static void menu_mtb(MENUENTRY *me) {current_action=A_MTB; set_cursor("pointer"); update_statusline();}
static void menu_add(MENUENTRY *me) {current_action=A_ADD; set_cursor("add"); update_statusline();}
static void menu_add_group(MENUENTRY *me){current_action=A_ADDGROUP;set_cursor("add"); update_statusline();}
static void menu_delete(MENUENTRY *me) {current_action=A_DELETE; set_cursor("delete"); update_statusline();}
static void menu_edit_prop(MENUENTRY *me){current_action=A_EDIT; set_cursor("edit"); update_statusline();}
static void menu_set_foreground(MENUENTRY *me){current_action=A_SFGC; set_cursor("color"); update_statusline();}
static void menu_set_background(MENUENTRY *me){current_action=A_SBGC; set_cursor("color"); update_statusline();}
static void menu_set_font(MENUENTRY *me) {current_action=A_SFONT; set_cursor("pointer"); update_statusline();}
static void menu_set_topic(MENUENTRY *me) {current_action=A_STOPIC; set_cursor("pointer"); update_statusline();}
static void menu_undodelete(MENUENTRY *me) {
if(undo_element) {
printf("Restore element...\n");
add_element(maindash,undo_element);
undo_element=NULL;
is_modified=1;
redraw_panel(drawing_area);
}
}
int add_element_group(char *text) {
if(!text || !*text) return(0);
if(verbose>0) printf("add_element_group: <%s>\n",text);
ELEMENT elc;
char *p1,*p2;
int done=0;
p2=p1=text;
while(!done) {
while(*p2 && *p2!='\n') p2++;
if(*p2==0) done=1;
else *p2=0;
bzero(&elc,sizeof(ELEMENT));
if(p1!=p2) {
init_element(&elc,p1);
if(verbose>=0) printf("Add Element: <%s>\n",p1);
add_element(maindash,&elc);
}
p1=++p2;
}
return(1);
}
static void menu_paste(MENUENTRY *me) {
GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gchar *text = gtk_clipboard_wait_for_text(clip);
printf("Clipboard: <%s>\n",text);
if(add_element_group(text)) {
is_modified=1;
redraw_panel(drawing_area);
}
}
static void menu_elements(char *typ) {
int i;
for(i=0;i<anzeltyp;i++) {
if(!strcmp(typ,eltyps[i].name)) current_element=i;
}
if(current_element>=0) menu_add(NULL);
}
static void menu_groups(char *typ) {
int i;
for(i=0;i<anzgroups;i++) {
if(!strcmp(typ,groups[i].name)) current_group=i;
}
if(current_group>=0) menu_add_group(NULL);
}
static void about_dialog(MENUENTRY *me) {
message_dialog(PACKAGE_NAME " Info",
PACKAGE_NAME "\n===============\n\n Version " VERSION "\n\n"
VERSION_DATE "\n\nDash-Designer\n\n (c) by Markus Hoffmann et. al.\n",1);
}
static void menu_new(MENUENTRY *me) {
/* Check if current file is nodified.
If so, open a warning dialog and offer
to save the file
*/
if(is_modified) emergency_save_dialog();
/* Then free the menu and create a new one. */
snprintf(ifilename,SIZEOF_IFILENAME,"new.dash");
free_dash(maindash);
maindash=new_dash(ifilename);
init_newloaded_dash();
}
static void menu_load(MENUENTRY *me) {
char newdash[256];
newdash[0]=0;
/* Check if current file is nodified. If so open Warning dialog and offer to save the file */
if(is_modified) emergency_save_dialog();
/* open a file selector to select a new dash to display.*/
int rc=fileselect_dialog(newdash,dashboarddir,"*.dash");
if(rc && newdash[0]) {
if(exist(newdash)) snprintf(ifilename,SIZEOF_IFILENAME,"%s",newdash);
else snprintf(ifilename,SIZEOF_IFILENAME,"%s/%s",dashboarddir,newdash);
free_dash(maindash);
if(exist(ifilename)) {
maindash=load_dash(ifilename);
} else {
maindash=new_dash(ifilename);
printf("ERROR: could not load %s. New!\n",ifilename);
}
init_newloaded_dash();
}
}
static void menu_merge(MENUENTRY *me) {
char newdash[256];
char buf[256];
newdash[0]=0;
/* open a file selector to select a new dash to display.*/
int rc=fileselect_dialog(newdash,dashboarddir,"*.dash");
if(rc && newdash[0]) {
if(exist(newdash)) snprintf(buf,sizeof(buf),"%s",newdash);
else snprintf(buf,sizeof(buf),"%s/%s",dashboarddir,newdash);
if(exist(buf)) {
maindash=merge_dash(maindash,buf);
init_newloaded_dash();
}
}
}
static void menu_save(MENUENTRY *me) {
if(is_modified || 1) { /* Do it anyways... */
save_dash(maindash,ifilename);
is_modified=0;
update_title(ifilename);
}
}
static void menu_save_as(MENUENTRY *me) {
char newdash[256];
newdash[0]=0;
/* open a file selector to select a new dash save to.*/
int rc=fileselect_dialog(newdash,dashboarddir,"*.dash");
if(rc && newdash[0]) {
if(exist(newdash)) {
char buffer[256];
snprintf(buffer,sizeof(buffer),"WARNING:\nSelected file \n%s \ndoes already exist.\n"
"Do you want to overwrite it now?\n\n",
newdash);
if(message_dialog("MQTT Hyperdash Dashdesign Warning",buffer,2)!=1) return;
}
snprintf(ifilename,SIZEOF_IFILENAME,"%s",newdash);
menu_save(me);
}
}
static void menu_start_hyperdash(MENUENTRY *me) {
if(is_modified) emergency_save_dialog();
if(is_modified) {
char newname[strlen(ifilename)+12];
strcpy(newname,ifilename);
strcat(newname,".autosave");
call_a_dash(newname);
} else call_a_dash(ifilename);
}
static void menu_quit(MENUENTRY *me) {
/* Check if current file is nodified. If so open Warning dialog and offer to save the file */
if(is_modified) emergency_save_dialog();
gtk_main_quit();
}
static void menu_grid_onoff(MENUENTRY *me) {
do_grid=(int)gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(me->widget));
if(pixmap) g_object_unref(pixmap);
pixmap=NULL;
update_drawarea();
}
static void menu_select_fgc(MENUENTRY *me) {
char buf[64];
snprintf(buf,sizeof(buf),"$%s",tohex(current_fgc));
int rc=colorselect_dialog("Select foregroud color",buf);
if(rc) current_fgc=(long)myatof(buf);
}
static void menu_select_bgc(MENUENTRY *me) {
char buf[64];
snprintf(buf,sizeof(buf),"$%s",tohex(current_bgc));
int rc=colorselect_dialog("Select backgroud color",buf);
if(rc) current_bgc=(long)myatof(buf);
}
/* open a file selector to select a new font.*/
static void menu_select_font(MENUENTRY *me) {
char buf[256];
sprintf(buf,"%s.ttf",current_font);
int rc=fileselect_dialog(buf,fontdir,"*.ttf");
if(rc && *buf) {
if(!fnmatch("*.ttf",buf,FNM_NOESCAPE)) buf[strlen(buf)-4]=0;
if(!strncmp(buf,fontdir,strlen(fontdir))) sprintf(current_font,"%s",buf+strlen(fontdir)+1);
else sprintf(current_font,"%s",buf);
}
}
/* open a file selector to select a new font.*/
static void menu_select_topic(MENUENTRY *me) {
char buf[256];
sprintf(buf,"%s",current_topic);
int rc=listselect_dialog(buf);
if(rc && *buf) {
sprintf(current_topic,"%s",buf);
}
}
static void menu_edit_broker(MENUENTRY *me) {
int k=0;
for(k=0;k<maindash->anzelement;k++) {
if((maindash->tree[k].type&EL_BROKER)==EL_BROKER) {
ELEMENT *el=&(maindash->tree[k]);
if(edit_element(el)) {
is_modified=1;
printf("Broker changed: %s %s %s\n",el->filename,el->text, el->format);
if(el->text && (el->text[0]==0 || !strcmp(el->text,"(null)"))) {
free(el->text);
el->text=NULL;
}
if(el->format && (el->format[0]==0 || !strcmp(el->format,"(null)"))) {
free(el->format);
el->format=NULL;
}
printf("Broker changed: %s \n",element2a(el));
}
return;
}
}
}
static MENUENTRY menuentries[]={
{0,"New", menu_new,NULL},
{0,"-------------",NULL,NULL},
{0,"Load ...", menu_load,NULL},
{0,"Merge ...", menu_merge,NULL},
{0,"-------------",NULL,NULL},
{0,"Save", menu_save,NULL},
{0,"Save As ...", menu_save_as,NULL},
{0,"-------------",NULL,NULL},
{0,"Run Dashboard",menu_start_hyperdash,NULL},
{0,"-------------",NULL,NULL},
{0,"Quit", menu_quit,NULL},
{MENU_TITLE,"File",NULL,NULL},
{0,"Undo delete", menu_undodelete,NULL},
{0,"Delete Element", menu_delete,NULL},
//{0,"Copy Elements", menuitem_response,NULL},
{0,"-------------",NULL,NULL},
{0,"Paste Elements", menu_paste,NULL},
{0,"-------------",NULL,NULL},
{0,"Edit Broker ...",menu_edit_broker,NULL},
{0,"-------------",NULL,NULL},
{0,"Edit Properties ...",menu_edit_prop,NULL},
{MENU_TITLE,"Edit",NULL,NULL},
{0,"# elements", menu_elements,NULL},
{MENU_TITLE,"Element",NULL,NULL},
#if 0
//{0,"Analog H scaler input with ticker", menu_,NULL},
//{0,"Analog V scaler input with ticker", menu_,NULL},
{0,"-------------",NULL,NULL},
//{0,"Analog value display", menu_,NULL},
//{0,"Digital value display", menu_,NULL},
//{0,"Analog Meter with value display", menu_,NULL},
#endif
{0,"% groups", menu_groups,NULL},
{MENU_TITLE,"Groups",NULL,NULL},
{0,"Identify Elements", menu_identify,NULL},
{0,"-------------",NULL,NULL},
{0,"Move Element", menu_move,NULL},
{0,"Copy Element", menu_copy,NULL},
{0,"Resize Element", menu_resize,NULL},
{0,"-------------",NULL,NULL},
{0,"Move to Background",menu_mtb,NULL},
{0,"-------------",NULL,NULL},
//{0,"Rotate left", menuitem_response,NULL},
//{0,"Rotate right", menuitem_response,NULL},
{0,"-------------",NULL,NULL},
//{0,"Change Text", menuitem_response,NULL},
//{0,"Change Format",menuitem_response,NULL},
{0,"-------------",NULL,NULL},
{0,"Set foreground color",menu_set_foreground,NULL},
{0,"Set background color",menu_set_background,NULL},
{0,"Set font", menu_set_font,NULL},
{0,"Set topic", menu_set_topic,NULL},
{0,"-------------",NULL,NULL},
//{0,"Select Layout",menuitem_response,NULL},
{0,"-------------",NULL,NULL},
{0,"Edit Element",menu_edit_prop,NULL},
{0,"-------------",NULL,NULL},
{0,"Add Element", menu_add,NULL},
{0,"Add Element Group", menu_add_group,NULL},
{MENU_TITLE,"Action",NULL,NULL},
{0,"[X] Grid",menu_grid_onoff,NULL},
{0,"-------------",NULL,NULL},
{0,"Select foreground color ...",menu_select_fgc,NULL},
{0,"Select background color ...",menu_select_bgc,NULL},
{0,"Select text font ...", menu_select_font,NULL},
{0,"Select topic ...", menu_select_topic,NULL},
{MENU_TITLE,"Settings",NULL,NULL},
{0,"About " PACKAGE_NAME " DashDesign ...",about_dialog,NULL},
{MENU_TITLE,"About",NULL,NULL},
{0,NULL,NULL,NULL}
};
/* Set the default menu states....*/
void menu_set_default() {
menu_identify(NULL); /* Set initial Action and mouse cursor. */
}