New upstream version 3.1.1
[xfishtank.git] / src / ui.c
diff --git a/src/ui.c b/src/ui.c
new file mode 100644 (file)
index 0000000..5018085
--- /dev/null
+++ b/src/ui.c
@@ -0,0 +1,586 @@
+/* -copyright-
+#-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan, 
+#-#    Vincent Renardias, Willem Vermin
+#-# 
+#-# Permission is hereby granted, free of charge, to any person 
+#-# obtaining a copy of this software and associated documentation 
+#-# files (the “Software”), to deal in the Software without 
+#-# restriction, including without limitation the rights to use, 
+#-# copy, modify, merge, publish, distribute, sublicense, and/or 
+#-# sell copies of the Software, and to permit persons to whom 
+#-# the Software is furnished to do so, subject to the following 
+#-# conditions:
+#-# 
+#-# The above copyright notice and this permission notice shall 
+#-# be included in all copies or substantial portions of the Software.
+#-# 
+#-# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 
+#-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
+#-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+#-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
+#-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
+#-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+#-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
+#-# OTHER DEALINGS IN THE SOFTWARE.
+#-# 
+*/
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include "ui_xml.h"
+#include "ui.h"
+#include "debug.h"
+#include "xfishtank.h"
+#include "utils.h"
+#include "fishes.h"
+
+//#include "xfishtank.xpm"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __cplusplus
+#define MODULE_EXPORT extern "C" G_MODULE_EXPORT
+#else
+#define MODULE_EXPORT G_MODULE_EXPORT
+#endif
+
+int FlagsChanged = 1;
+
+static int    human_action = 1;   /* is it a human who is playing with the buttons? */
+
+#define NTYPES 16 
+static GtkBuilder      *builder;
+static GtkWidget       *hauptfenster;
+static GtkStyleContext *hauptfenstersc;
+static char             buf[100];
+static GtkWidget       *id_fishes;
+static GtkWidget       *id_fishesvalue;
+static GtkWidget       *id_speed;
+static GtkWidget       *id_speedvalue;
+static GtkWidget       *id_bubbles;
+//static GtkWidget       *id_squish;
+static GtkWidget       *id_logo1;
+static GtkWidget       *id_logo2;
+static GtkWidget       *id_logo3;
+static GtkWidget       *id_bubble_color;
+static GtkWidget       *id_bg_color;
+static GtkWidget       *id_bubblesvalue;
+//static GtkWidget       *id_squish_picture;
+
+static int logo[3];
+static int flip[3];
+
+static void   handle_css(void);
+static void   init_ids(void);
+static void   init_pixmaps(void);
+static FILE  *openflagsfile(char *mode);
+static float  scale_to_bubblesfactor(float scale);
+static void   ab(float Max, float Min, float *a, float *b);
+static float  fishes_to_scale(float n);
+static float  bubblesfactor_to_scale(float n);
+static float  scale_to_fishes(float scale);
+static float  speedfactor_to_scale(float n);
+static float  scale_to_speedfactor(float scale);
+static void   init_pixmap(GtkWidget *id, int n, int flip);
+static int    do_animate(void *dummy);
+
+
+// Sometimes it is good to have a logarithmic scale, such that
+//
+//  V = a*M*10**s + b 
+//
+//  where:       V = parameter (e.g. SpeedFactor)
+//               M = desired maximum of V (e.g. 4.0)
+//               s = value of the gtkscale (0 .. 1.0)
+//  Furthermore: m = desired minimum of V (e.g. 0.2)
+//
+//  Then:
+//     a = (M - m)/(9*M)
+//     b = m - a*M
+//
+// The placement of a is a logical choice, the placement of b is 
+// more or less random. I need a constant next to a, because I want
+// to define minimum V AND maximum V. 
+//
+// Given V, compute s:
+//
+//   s = log10((V-b)/(a*M))
+// 
+
+void ab(float Max, float Min, float *a, float *b)
+{
+   *a = (Max - Min)/(9.0*Max);
+   *b = Min - (*a)*Max;
+}
+
+static const float MaxBubbles = 500;
+static const float MinBubbles = 1;
+
+static const float MaxFishes  = 200;
+static const float MinFishes  = 1.0;
+
+static const float MaxSpeed   = 400;
+static const float MinSpeed   = 30;
+
+void handle_css()
+{
+}
+
+
+void show_dialog(int type, const char *format, const char *text)
+{
+   GtkMessageType message_type;
+   if (type == 1)
+      message_type = GTK_MESSAGE_ERROR;
+   else
+      message_type = GTK_MESSAGE_INFO;
+
+   GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(hauptfenster),
+        GTK_DIALOG_MODAL,
+        message_type,
+        GTK_BUTTONS_OK,
+        format,
+        text
+        );
+   g_signal_connect(m,"response",G_CALLBACK(gtk_main_quit),NULL);
+   gtk_widget_show_all(m);
+}
+
+void init_ids()
+{
+   id_fishes         = GTK_WIDGET(gtk_builder_get_object(builder, "id-fishes"));
+   id_fishesvalue    = GTK_WIDGET(gtk_builder_get_object(builder, "id-fishesvalue"));
+   id_speed          = GTK_WIDGET(gtk_builder_get_object(builder, "id-speed"));
+   id_speedvalue     = GTK_WIDGET(gtk_builder_get_object(builder, "id-speedvalue"));
+   id_logo1          = GTK_WIDGET(gtk_builder_get_object(builder, "id-logo1"));
+   id_logo2          = GTK_WIDGET(gtk_builder_get_object(builder, "id-logo2"));
+   id_logo3          = GTK_WIDGET(gtk_builder_get_object(builder, "id-logo3"));
+   id_bubble_color   = GTK_WIDGET(gtk_builder_get_object(builder, "id-bubble-color"));
+   id_bg_color       = GTK_WIDGET(gtk_builder_get_object(builder, "id-bg-color"));
+   id_bubbles        = GTK_WIDGET(gtk_builder_get_object(builder, "id-bubbles"));
+   //id_squish         = GTK_WIDGET(gtk_builder_get_object(builder, "id-squish"));
+   id_bubblesvalue   = GTK_WIDGET(gtk_builder_get_object(builder, "id-bubblesvalue"));
+   //id_squish_picture = GTK_WIDGET(gtk_builder_get_object(builder, "id-squish-picture"));
+}
+
+int do_animate(void *dummy)
+{
+   (void)dummy;
+   int m = drand48()*3;
+   P("m: %d %d\n",m, logo[m]);
+   // needs adjustment if NUM_FRAMES != 2
+   if (logo[m]%NUM_FRAMES)
+      logo[m]--;
+   else
+      logo[m]++;
+   GtkWidget *id;
+   switch(m)
+   {
+      case 0: 
+        id = id_logo1;
+        break;
+      case 1: 
+        id = id_logo2;
+        break;
+      case 2: 
+        id = id_logo3;
+        break;
+   }
+   init_pixmap(id, logo[m], flip[m]);
+
+   return TRUE;
+}
+
+void init_pixmap(GtkWidget *id, int n, int flip)
+{
+   int w,h;
+   float w0,h0;
+
+   GdkPixbuf *pixbuf, *pixbuf1, *pixbuf2;
+   pixbuf  = gdk_pixbuf_new_from_xpm_data ((const char **)fishes[n]);
+   w0 = gdk_pixbuf_get_width(pixbuf);
+   h0 = gdk_pixbuf_get_height(pixbuf);
+   if (w0 > h0)
+   {
+      w = 64;
+      h = h0/w0*w;
+   }
+   else
+   {
+      h = 64;
+      w = w0/h0*h;
+   }
+
+   pixbuf1 = gdk_pixbuf_scale_simple(pixbuf,w,h,GDK_INTERP_BILINEAR);
+   g_object_unref(pixbuf);
+
+   if(flip)
+   {
+      pixbuf2 = gdk_pixbuf_flip(pixbuf1,1);
+      gtk_image_set_from_pixbuf(GTK_IMAGE(id),pixbuf2);
+      g_object_unref(pixbuf2);
+   }
+   else
+      gtk_image_set_from_pixbuf(GTK_IMAGE(id),pixbuf1);
+
+   g_object_unref(pixbuf1);
+}
+
+void init_pixmaps()
+{
+   int i;
+   for (i=0; i<3; i++)
+   {
+      logo[i] = NUM_FRAMES*(int)(drand48()*NUM_FISH);
+      flip[i] = drand48()*2;
+   }
+   init_pixmap(id_logo1,logo[0],flip[0]);
+   init_pixmap(id_logo2,logo[1],flip[1]);
+   init_pixmap(id_logo3,logo[2],flip[2]);
+}
+
+void set_buttons()
+{
+   int h = human_action;
+   human_action = 0;
+
+   gtk_range_set_value(GTK_RANGE(id_fishes), fishes_to_scale(flimit));
+   sprintf(buf,"%d",flimit);
+   gtk_label_set_text(GTK_LABEL(id_fishesvalue),buf);
+
+   gtk_range_set_value(GTK_RANGE(id_bubbles),bubblesfactor_to_scale(blimit));
+   sprintf(buf,"%d",(int)(blimit));
+   gtk_label_set_text(GTK_LABEL(id_bubblesvalue),buf);
+
+   gtk_range_set_value(GTK_RANGE(id_speed),speedfactor_to_scale(100*speedfactor));
+   P("speedfactor: %f %f\n",speedfactor, speedfactor_to_scale(100*speedfactor));
+   sprintf(buf,"%d",(int)(100*speedfactor));
+   gtk_label_set_text(GTK_LABEL(id_speedvalue),buf);
+
+   GdkRGBA color;
+   P("bcolor: %s\n",bcolorstring);
+   gdk_rgba_parse(&color, bcolorstring);
+   gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(id_bubble_color),&color);
+
+   P("bgcolor: %s\n",bgcolorstring);
+   gdk_rgba_parse(&color, bgcolorstring);
+   gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(id_bg_color),&color);
+
+   human_action = h;
+
+   FlagsChanged = 1;
+}
+
+
+MODULE_EXPORT void button_fishes(GtkWidget *w)
+{
+   if (!human_action)
+      return;
+   float value = gtk_range_get_value(GTK_RANGE(w));
+   P("fishes: %f\n",value);
+   create_fishes(scale_to_fishes(value));
+   FlagsChanged = 1;
+
+   sprintf(buf,"%d",flimit);
+   gtk_label_set_text(GTK_LABEL(id_fishesvalue),buf);
+}
+
+MODULE_EXPORT void button_bubbles(GtkWidget *w)
+{
+   if (!human_action)
+      return;
+   float value = gtk_range_get_value(GTK_RANGE(w));
+   P("bubbles: %f %d\n",value,blimit);
+   create_bubbles(scale_to_bubblesfactor(value));
+   FlagsChanged = 1;
+
+   sprintf(buf,"%d",blimit);
+   gtk_label_set_text(GTK_LABEL(id_bubblesvalue),buf);
+}
+
+MODULE_EXPORT void button_speed(GtkWidget *w)
+{
+   if (!human_action)
+      return;
+   float value = gtk_range_get_value(GTK_RANGE(w));
+   P("speedfactor: %f %f\n",value,speedfactor);
+   setspeed(scale_to_speedfactor(value));
+   P("speedfactor: %f\n",speedfactor);
+   FlagsChanged = 1;
+
+   sprintf(buf,"%d",(int)(100*speedfactor));
+   gtk_label_set_text(GTK_LABEL(id_speedvalue),buf);
+}
+
+
+MODULE_EXPORT void button_defaults()
+{
+   set_defaults();
+   create_fishes(flimit);
+   create_bubbles(blimit);
+   setbcolor();
+   setbgcolor();
+   setspeed(speedfactor);
+   if (!human_action)
+      return;
+   set_buttons();
+}
+
+#if 0
+MODULE_EXPORT void button_squish(GtkWidget *w)
+{
+   if (!human_action)
+      return;
+   int value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+   P("squish: %d\n",value);
+   handle_squish(value);
+   FlagsChanged = 1;
+}
+#endif
+MODULE_EXPORT void button_iconify()
+{
+   if (!human_action)
+      return;
+   gtk_window_iconify(GTK_WINDOW(hauptfenster));
+}
+
+MODULE_EXPORT void button_bubble_color(GtkWidget *w)
+{
+   GdkRGBA color;
+   if (!human_action)
+      return;
+   gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
+   free(bcolorstring);
+   rgba2color(&color,&bcolorstring);
+   setbcolor();
+   FlagsChanged = 1;
+}
+
+MODULE_EXPORT void button_bg_color(GtkWidget *w)
+{
+   GdkRGBA color;
+   if (!human_action)
+      return;
+   gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
+   free(bgcolorstring);
+   rgba2color(&color,&bgcolorstring);
+   setbgcolor();
+   FlagsChanged = 1;
+}
+
+#if 0
+MODULE_EXPORT void button_guts_color(GtkWidget *w)
+{
+   GdkRGBA color;
+   if (!human_action)
+      return;
+   gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
+   free(gutsColor);
+   rgba2color(&color,&gutsColor);
+   setgutsgcColor();
+   FlagsChanged = 1;
+}
+#endif
+
+#if 0
+MODULE_EXPORT void button_squish_color(GtkWidget *w)
+{
+   GdkRGBA color;
+   if (!human_action)
+      return;
+   gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
+   free(gutsColor);
+   rgba2color(&color,&gutsColor);
+}
+#endif
+
+FILE *openflagsfile(char *mode)
+{
+   char *h = getenv("HOME");
+   if (h == NULL)
+      return NULL;
+   char *flagsfile = (char*)malloc((strlen(h)+strlen(FLAGSFILE)+2)*sizeof(char));
+   flagsfile[0] = 0;
+   strcat(flagsfile,h);
+   strcat(flagsfile,"/");
+   strcat(flagsfile,FLAGSFILE);
+   FILE *f = fopen(flagsfile,mode);
+   P("openflagsfile %s %s\n",flagsfile,mode);
+   free(flagsfile);
+   return f;
+}
+
+void ReadFlags()
+{
+   FILE *f = openflagsfile(_("r"));
+   if (f == NULL)
+   {
+      I("Cannot read $HOME/%s\n",FLAGSFILE);
+      return;
+   }
+   int lineno     = 1;
+   while(1)
+   {
+      char *line = NULL;
+      size_t n = 0;
+      int m = getline(&line,&n,f);
+      if (m<0)
+        break;
+      P("ReadFlags: %d [%s]\n",lineno,line);
+      char *flag = (char*)malloc((strlen(line)+1)*sizeof(char));
+      m = sscanf(line, "%s", flag);
+      if (m == EOF || m == 0)
+        continue;
+      char *rest = line + strlen(flag);
+
+      char *p;
+      p = rest;
+      while (*p == ' ' || *p == '\t' || *p == '\n')
+        p++;
+      rest = p;
+      p = &line[strlen(line)-1];
+      while (*p == ' ' || *p == '\t' || *p == '\n')
+        p--;
+      *(p+1) = 0;
+
+      P("ReadFlags: %s [%s]\n",flag,rest);
+      if(!strcmp(flag,"fishes"))
+      {
+        flimit = atoi(rest);
+        P("flimit: %d\n",flimit);
+      }
+      else if(!strcmp(flag,"bubbles"))
+      {
+        blimit = atoi(rest);
+        P("bubbles: %d\n",blimit);
+      }
+      else if(!strcmp(flag,"speed"))
+      {
+        speedfactor = atoi(rest)/100.0;
+        P("speedfactor: %d\n",(int)(100*speedfactor));
+      }
+      else if(!strcmp(flag,"bc"))
+      {
+        if(bcolorstring)
+           free(bcolorstring);
+        bcolorstring = strdup(rest);
+        P("bcolorstring: %s\n",bcolorstring);
+      }
+      else if(!strcmp(flag,"bgc"))
+      {
+        if(bgcolorstring)
+           free(bgcolorstring);
+        bgcolorstring = strdup(rest);
+        P("bcolorstring: %s\n",bgcolorstring);
+      }
+      lineno++;
+      free(line);
+      free(flag);
+   }
+   fclose(f);
+}
+
+void WriteFlags()
+{
+   FILE *f = openflagsfile(_("w"));
+   if (f == NULL)
+   {
+      I("Cannot write $HOME/%s\n",FLAGSFILE);
+      return;
+   }
+   fprintf(f, "fishes      %d\n", flimit);
+   fprintf(f, "bubbles     %d\n", blimit);
+   if (bcolorstring && strlen(bcolorstring))
+      fprintf(f, "bc          %s\n", bcolorstring);
+   if(bgcolorstring && strlen(bgcolorstring))
+      fprintf(f, "bgc         %s\n", bgcolorstring);
+   fprintf(f, "speed       %d\n", (int)(100*speedfactor));
+   fclose(f);
+   init_pixmaps();
+}
+
+float bubblesfactor_to_scale(float n)
+{
+   float a,b;
+   ab(MaxBubbles, MinBubbles,&a,&b);
+   return mylog10f((n-b)/(a*MaxBubbles));
+}
+
+
+float scale_to_bubblesfactor(float scale)
+{
+   float a,b;
+   ab(MaxBubbles, MinBubbles,&a,&b);
+   return a * MaxBubbles*myexp10f(scale) + b; 
+}
+
+float fishes_to_scale(float n)
+{
+   float a,b;
+   ab(MaxFishes, MinFishes, &a, &b);
+   float rc = mylog10f((n-b)/(a*MaxFishes));
+   P("ftos %f %f\n",n,rc);
+   return rc;
+}
+
+float scale_to_fishes(float scale)
+{
+   float a,b;
+   ab(MaxFishes, MinFishes, &a, &b);
+   return a * MaxFishes*myexp10f(scale) + b; 
+}
+
+float speedfactor_to_scale(float n)
+{
+   float a,b;
+   ab(MaxSpeed, MinSpeed, &a, &b);
+   float rc = mylog10f((n-b)/(a*MaxSpeed));
+   P("stos %f %f\n",n,rc);
+   return rc;
+}
+
+float scale_to_speedfactor(float scale)
+{
+   float a,b;
+   ab(MaxSpeed, MinSpeed, &a, &b);
+   return 0.01*(a * MaxSpeed*myexp10f(scale) + b); 
+}
+
+void iconify()
+{
+   gtk_window_iconify(GTK_WINDOW(hauptfenster));
+}
+
+void ui()
+{
+   P("here is uiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\n");
+
+   builder = gtk_builder_new_from_string (xfishtank_xml, -1);
+   gtk_builder_connect_signals (builder, builder);
+   hauptfenster  = GTK_WIDGET(gtk_builder_get_object(builder, "hauptfenster"));
+
+   hauptfenstersc  = gtk_widget_get_style_context(hauptfenster);
+
+   handle_css();
+   char wtitle[100];
+   wtitle[0] = 0;
+   strcat(wtitle,"XfishtanK");
+#ifdef HAVE_CONFIG_H
+   strcat(wtitle,"-");
+   strncat(wtitle,VERSION,99 - strlen(wtitle));
+#endif
+   gtk_window_set_title(GTK_WINDOW(hauptfenster),wtitle);
+   gtk_window_set_resizable(GTK_WINDOW(hauptfenster),False);
+   gtk_widget_show_all (hauptfenster);
+   g_signal_connect (GTK_WINDOW(hauptfenster), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
+   g_signal_connect (GTK_WINDOW(hauptfenster), "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+   init_ids();
+
+   init_pixmaps();
+   set_buttons();
+   g_timeout_add_full(G_PRIORITY_DEFAULT, 150,   do_animate        ,NULL, NULL);
+}
+
This page took 0.017167 seconds and 4 git commands to generate.