/********************************************************************************
   zfuncs.h      include file for zfuncs functions

   Copyright 2007-2024 Michael Cornelison
   source code URL: https://kornelix.net
   contact: mkornelix@gmail.com

   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 3 of the License, or
   (at your option) any later version. See https://www.gnu.org/licenses

   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.

*********************************************************************************/

#include <sys/sysinfo.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <sys/utsname.h>
#include <malloc.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <execinfo.h>
#include <locale.h>
#include <glob.h>
#include <gtk/gtk.h>
#include <clutter-gtk/clutter-gtk.h>

#define VERTICAL GTK_ORIENTATION_VERTICAL                                        //  GTK shortcuts
#define HORIZONTAL GTK_ORIENTATION_HORIZONTAL
#define PIXBUF GdkPixbuf
#define GDKCOLOR GdkColorspace

#define  int8    ch                                                              //  number types
#define  int16   short
#define  int32   int
#define  int64   long long                                                       //  long long is always 64 bits
#define  uint8   unsigned char
#define  uint16  unsigned short
#define  uint32  unsigned int
#define  uint64  unsigned long long

#define  ch      char
#define  uch     unsigned char
#define  cch     const char

#define  VOL volatile

#define  STATB  struct stat                                                      //  stat() file status buffer

#define  mutex_t        pthread_mutex_t                                          //  abbreviations
#define  mutex_init     pthread_mutex_init
#define  mutex_lock     pthread_mutex_lock
#define  mutex_trylock  pthread_mutex_trylock
#define  mutex_unlock   pthread_mutex_unlock
#define  mutex_destroy  pthread_mutex_destroy

#define  XFCC 1000                                                               //  max. file pathname cc tolerated
#define  null NULL
#define  true 1
#define  false 0
#define  NOP

//  trace execution: source file, function, line no, caller address

#define TRACE trace(__FILE__,__FUNCTION__,__LINE__,__builtin_return_address(0));

//  system functions ============================================================

void *zmalloc(int64 cc, ch *tag);                                                //  malloc() wrapper
void zfree(void *pp);                                                            //  free() wrapper
void zmalloc_report(void *zd);                                                   //  memory usage by tag > popup report
void zmalloc_growth(void *zd);                                                   //  memory growth by tag > popup report
int  zmalloc_test(int64 cc);                                                     //  test if cc free memory available
double realmemory();                                                             //  get available real memory in MB units
double availmemory();                                                            //  get available memory (+swap) in MB units
ch * zstrdup(ch *zstring, ch *tag, int addcc = 0);                               //  strdup() wrapper with added space
int  zstrcopy(ch *&zstring, ch *string, ch *tag, int addcc = 0);                 //  replace zstring with string + added cc
void Plog(int lev, ch *format, ...);                                             //  add message to stdout log file, fflush()
void xmessage(ch *message);                                                      //  popup message not needing GTK
void zexit(int popup, ch *message, ...);                                         //  exit a process and kill child processes
void zbacktrace();                                                               //  produce a backtrace to stdout
void zappcrash(ch *format, ...);                                                 //  crash with popup message in text window
void catch_signals();                                                            //  catch signals and do backtrace dump
void trace(ch *file, ch *func, int line, void *addr);                            //  implements TRACE macro
void tracedump();                                                                //  dump program trace data
void beroot(int argc, ch *argv[]);                                               //  restart program as root if password OK
int  runroot(ch *command);                                                       //  run command as root via su or sudo
double get_seconds();                                                            //  real seconds since 2000.01.01
double get_seconds(timespec *ts);                                                //  real seconds from timespec struct
double get_modtime(char *file);                                                  //  file mod time in real seconds
void logtime_init(ch *text);                                                     //  set initial time
void logtime(ch *text);                                                          //  log elapsed time since prior call
void start_timer(double &time0);                                                 //  start a timer
double get_timer(double &time0);                                                 //  get elapsed time in seconds
void start_CPUtimer(double &time0);                                              //  start a process CPU timer
double get_CPUtimer(double &time0);                                              //  get elapsed CPU time, seconds
double CPUtime();                                                                //  get elapsed process CPU time
int  memused();                                                                  //  get process real memory, MB
int  proc_resources(double &cpu, double &bio);                                   //  get process CPU and block IO counts
void compact_time(time_t DT, ch *compactDT);                                     //  time_t DT to yyyymmddhhmmss
void pretty_datetime(time_t DT, ch *prettyDT);                                   //  time_t DT to yyyy-mm-dd hh:mm:ss
void secs_datetime(double secs, int datetime[6]);                                //  seconds to year/mon/day/hour/min/secs
void datetime_secs(int datetime[6], double *secs);                               //  year/mon/day/hour/min/secs to seconds
int  parseprocfile(ch *pfile, ch *pname, double *value, ...);                    //  get data from /proc file
int  parseprocrec(ch *prec, int field, double *value, ...);                      //  get data from /proc file record
int  get_smp_counts(int &Pcores, int &Ecores);                                   //  return processor core counts
int  coretemp();                                                                 //  get CPU temperature (package temp)
int  disktemp(ch *disk);                                                         //  get disk temp, e.g. "/dev/sda"
void zsleep(double dsecs);                                                       //  sleep specified seconds
void zloop(double dsecs);                                                        //  loop specified seconds
void spinlock(int lock);                                                         //  lock/unlock enclosed code block
int  global_lock(ch *lockfile);                                                  //  obtain exclusive lock, multi-process
void global_unlock(int fd, ch *lockfile);                                        //  release the lock
int  resource_lock(int &resource);                                               //  simple lock/unlock usable with GTK
void resource_unlock(int &resource);                                             //  (never waits, returns lock status)
int  zget_locked(int &param);                                                    //  lock and get multi-thread parameter
void zput_locked(int &param, int value);                                         //  put and unlock multi-thread parameter
int  zadd_locked(int &param, int incr);                                          //  increment multi-thread parameter
pthread_t start_detached_thread(void * tfunc(void *), void * arg);               //  start detached thread function
pthread_t start_Jthread(void * threadfunc(void *), void * arg);                  //  start joinable thread function
int  wait_Jthread(pthread_t tid);                                                //  wait for completion (join thread)
void synch_threads(int NT = 0);                                                  //  synchronize NT threads
int  main_thread();                                                              //  return 1 if main() thread, else 0
void set_cpu_affinity(int cpu);                                                  //  set cpu affinity for calling thread
int  zshell(ch *options, ch *command, ...);                                      //  do shell command and get status
ch * command_output(int &contx, ch *command, ...);                               //  get shell command output
int  command_status(int contx);                                                  //  get exit status of command
int  signalProc(ch * pname, ch * signal);                                        //  pause/resume/kill subprocess
ch * fgets_trim(ch * buff, int maxcc, FILE *, int bf = 0);                       //  fgets + trim trailing \n \r (blanks)
int  samefolder(ch *file1, ch *file2);                                           //  returns 1 if files in same folder
int  parsefile(ch *path, ch **dir, ch **file, ch **ext);                         //  parse a filespec
int  renamez(ch *file1, ch *file2);                                              //  rename, also across file systems
int  check_create_dir(ch *path);                                                 //  check if folder exists, ask to create
int  copyFile(ch *sfile, ch *dfile);                                             //  copy file to file or file to folder
int  cp_copy(ch *sfile, ch *dfile);                                              //  same, using shell "cp -f -p"
uint diskspace(ch *file);                                                        //  get disk space for given file, MB
ch * get_file_extension(ch *file);                                               //  get correct .ext if file.ext is wrong
int  zreaddir(ch *folder, ch **&files);                                          //  return all files in a folder, sorted
int  zreadfile(ch *filename, ch **&rrecs);                                       //  read file, return array of records
int  zwritefile(ch *filename, ch **&rrecs);                                      //  write array of records to file
void zreadfile_free(ch **&rrecs);                                                //  free zreadfile() memory
ch * combine_argvs(int argc, ch *argv[], int Nth);                               //  combine argv[ii] elements Nth to last

//  measure CPU time spent in a function or code block within a function

extern VOL double cpu_profile_timer;                                             //  internal data tables
extern VOL double cpu_profile_table[100];
extern VOL double cpu_profile_elapsed;
void cpu_profile_init();                                                         //  initialize at start of test
void cpu_profile_report();                                                       //  report CPU time per function
inline void cpu_profile_enter(int fnum)                                          //  at entry to measured code block
{  cpu_profile_timer = cpu_profile_elapsed;  }
inline void cpu_profile_exit(int fnum)                                           //  at exit from measured code block
{  cpu_profile_table[fnum] += cpu_profile_elapsed - cpu_profile_timer; }

int pagefaultrate();                                                             //  monitor own process hard fault rate

//  string macros and functions =================================================

#define strmatch(str1,str2) (! strcmp((str1),(str2)))                            //  TRUE if strings equal
#define strmatchN(str1,str2,cc) (! strncmp((str1),(str2),(cc)))                  //  TRUE if strings[cc] equal
#define strmatchcase(str1,str2) (! strcasecmp((str1),(str2)))                    //  TRUE if strings equal, ignoring case
#define strmatchcaseN(str1,str2,cc) (! strncasecmp((str1),(str2),(cc)))          //  TRUE if strings[cc] equal, ignoring case

ch * substringR(ch *string, ch *delims, int Nth);                                //  get Nth delimited substring, thread-safe
ch * substringR(ch *string, ch delim, int Nth);                                  //  same, single delimiter
ch * substring(ch *string, ch *delims, int Nth);                                 //  same, no zfree() needed, not thread-safe
ch * substring(ch *string, ch delim, int Nth);                                   //  same, single delimiter
int  strHash(ch *string, uint max);                                              //  string --> random int 0 to max-1
int  strncpy0(ch *dest, ch *source, uint cc);                                    //  strncpy, insure null, return 0 if fit
void strnPad(ch *dest, ch *source, int cc);                                      //  strncpy with blank padding to cc
int  strTrim(ch *dest, ch *source);                                              //  remove trailing blanks
int  strTrim(ch *string);                                                        //  remove trailing blanks
int  strTrim2(ch *dest, ch *source);                                             //  remove leading and trailing blanks
int  strTrim2(ch *string);                                                       //  remove leading and trailing blanks
int  strCompress(ch *dest, ch *source);                                          //  remove all blanks incl. embedded
int  strCompress(ch *string);                                                    //  remove all blanks
int  strncatv(ch *dest, int maxcc, ch *source, ...);                             //  catenate strings (last = null)
int  strmatchV(ch *string, ...);                                                 //  compare to N strings, return 1-N or 0
void strToUpper(ch *dest, ch *source);                                           //  move and conv. string to upper case
void strToUpper(ch *string);                                                     //  conv. string to upper case
void strToLower(ch *dest, ch *source);                                           //  move and conv. string to lower case
void strToLower(ch *string);                                                     //  conv. string to lower case
int  repl_1str(ch *strin, ch *strout, int maxcc, ch *ssin, ch *ssout);           //  copy string and replace 1 substring
int  repl_Nstrs(ch *strin, ch *strout, int maxcc, ...);                          //  copy string and replace N substrings
int  breakup_text(ch *in, ch **&out, ch *delims, int cc1, int cc2);              //  break long string into substrings
void strncpyx(ch *out, ch *in, int ccin);                                        //  conv. string to hex format
void StripZeros(ch *pNum);                                                       //  1.230000E+12 --> 1.23E+12
int  blank_null(ch *string);                                                     //  test for blank/null string
int  clean_escapes(ch *string);                                                  //  replace \x escapes with real characters
int  utf8len(ch *utf8string);                                                    //  get graphic cc for UTF8 string
int  utf8substring(ch *utf8out, ch *utf8in, int pos, int cc);                    //  get graphic substring from UTF8 string
int  utf8_check(ch *string);                                                     //  check utf8 string for encoding errors
int  utf8_position(ch *utf8in, int Nth);                                         //  get byte position of Nth graphic char.
int  zsed(ch *file, ...);                                                        //  replace string1/3... with string2/4...
ch * zstrstr(ch *haystack, ch *needle);                                          //  work like strstr() and strcasestr()
ch * zstrcasestr(ch *haystack, ch *needle);                                      //  (but "" does NOT match any string)
ch * zstrcpy(ch *dest, ch *source);                                              //  strcpy with overlap allowed
ch * zstrncpy(ch *dest, ch *source, int cc);                                     //  strncpy with overlap allowed
int  zstrcmp(ch *s1, ch *s2);                                                    //  like strcmp, but \n terminates like null
int  zstrcmp2(ch *s1, ch *s2);                                                   //  strcmp using ch *args, not cch*
int  zstrcasecmp(ch *s1, ch *s2);                                                //  strcasecmp using ch *args, not cch*
ch * zescape_quotes(ch *string);                                                 //  escape quotes for use in shell commands

//  number conversion ===========================================================

int convSI (ch *s, int &i, ch **delm = 0);                                       //  string to int
int convSI (ch *s, int &i, int low, int hi, ch **delm = 0);                      //  (with low/high limit checking)
int convSD (ch *s, double &d, ch **delm = 0);                                    //  string to double
int convSD (ch *s, double &d, double low, double hi, ch **delm = 0);             //  (with low/high limit checking)
int convSF (ch *s, float &f, ch **delm = 0);                                     //  string to double
int convSF (ch *s, float &f, float low, float hi, ch **delm = 0);                //  (with low/high limit checking)
int convIS (int iin, ch *outp, int *cc = 0);                                     //  int to string, returned cc
int convDS (double din, int prec, ch *outp, int *cc = 0);                        //  double to string, precision, output cc
double atofz(ch *string);                                                        //  atof() for comma/period decimal points
ch * formatKBMB(double fnum, int prec);                                          //  format nnn B, nn.n KB, n.nn MB, etc.

//  wildcard functions ==========================================================

int MatchWild(ch * wildstr, ch * str);                                           //  wildcard string match (match = 0)
int MatchWildCase(ch * wildstr, ch * str);                                       //  wildcard string match, ignoring case
ch * SearchWild(ch *wpath, int &flag);                                           //  wildcard file search
ch * SearchWildCase(ch *wpath, int &flag);                                       //  wildcard file search, ignoring case
int zfind(ch *pattern, ch **&flist, int &NF);                                    //  wildcard file search using glob()

//  search and sort functions ===================================================

int bsearch(int seekint, int nn, int list[]);                                    //  binary search sorted list[nn]
int bsearch(ch *seekrec, ch *allrecs, int recl, int nrecs);                      //  binary search sorted records
int bsearch(ch *seekrec, ch **allrecs, int N, int nrecs);                        //  binary search sorted pointers to recs

typedef int HeapSortUcomp(ch *rec1, ch *rec2);                                   //  return -1/0/+1 if rec1 </=/> rec2
void HeapSort(int vv[], int nn);                                                 //  Heap Sort - integer
void HeapSort(float vv[], int nn);                                               //  Heap Sort - float
void HeapSort(double vv[], int nn);                                              //  Heap Sort - double
void HeapSort(ch *vv[], int nn);                                                 //  Heap Sort - char *, ascending order
void HeapSort(ch *vv1[], ch *vv2[], int nn);                                     //  Heap Sort - parallel char *, ascending
void HeapSort(ch *vv[], int nn, HeapSortUcomp);                                  //  Heap Sort - char *, user-defined order
void HeapSort(ch *recs, int RL, int NR, HeapSortUcomp);                          //  Heap Sort - records, user-defined order

int MemSort(ch * RECS, int RL, int NR, int KEYS[][3], int NK);                   //  memory sort, records with multiple keys

int zmember(int testval, int matchval1, ...);                                    //  test if value matches any in a list

//  hash table class ============================================================

class HashTab
{
   static int tries1;                                                            //  insert tries
   static int tries2;                                                            //  find/delete tries
   int     cap;                                                                  //  table capacity
   int     count;                                                                //  strings contained
   int     cc;                                                                   //  string length
   ch      *table = 0;                                                           //  table[cc][cap]
public:
   HashTab(int cc, int cap);                                                     //  constructor
   ~HashTab();                                                                   //  destructor
   int Add(ch * string);                                                         //  add a new string
   int Del(ch * string);                                                         //  delete a string
   int Find(ch * string);                                                        //  find a string
   int GetCount() { return count; };                                             //  get string count
   int GetNext(int & first, ch * string);                                        //  get first/next string
   int Dump();                                                                   //  dump hash table
};

//  list processing functions ===================================================

typedef struct {                                                                 //  list data type
   int      count;                                                               //  count of member strings
   ch       **mber = 0;                                                          //  member strings, null == missing
}  zlist_t;

zlist_t * zlist_new(int count);                                                  //  create zlist with 'count' empty members
void zlist_free(zlist_t *zlist);                                                 //  delete zlist, free memory
void zlist_dump(zlist_t *zlist);                                                 //  dump zlist to stdout
int zlist_count(zlist_t *zlist);                                                 //  get zlist member count
ch * zlist_get(zlist_t *zlist, int Nth);                                         //  get a zlist member
void zlist_put(zlist_t *zlist, ch *string, int Nth);                             //  put a zlist member (replace existing)
void zlist_insert(zlist_t *zlist, ch *string, int Nth);                          //  insert Nth member, old Nth >> Nth+1
void zlist_remove(zlist_t *zlist, int Nth);                                      //  remove a zlist member (count -= 1)
void zlist_clear(zlist_t *zlist, int Nth);                                       //  clear zlist from Nth member to end
void zlist_purge(zlist_t *zlist);                                                //  purge zlist of all null members
int zlist_add(zlist_t *zlist, ch *string, int Funiq);                            //  add new member at first null or append
int zlist_append(zlist_t *zlist, ch *string, int Funiq);                         //  append new member at end (if unique)
int zlist_prepend(zlist_t *zlist, ch *string, int Funiq);                        //  prepend new member at posn 0 (if unique) 
int zlist_find(zlist_t *zlist, ch *string, int posn);                            //  find next match from posn
int zlist_findwild(zlist_t *zlist, ch *wstring, int posn);                       //  find next wildcard match from posn
zlist_t * zlist_copy(zlist_t *zlist1);                                           //  copy a zlist
zlist_t * zlist_insert_zlist(zlist_t *zlist1, zlist_t *zlist2, int Nth);         //  insert zlist2 into zlist1 at posn Nth
zlist_t * zlist_remove(zlist_t *zlist1, zlist_t *zlist2);                        //  remove all members of zlist2 from zlist1
void zlist_sort(zlist_t *zlist);                                                 //  sort zlist ascending
void zlist_sort(zlist_t *zlist, int ccfunc(ch *, ch *));                         //  sort zlist via caller compare function
int zlist_to_file(zlist_t *zlist, ch *filename);                                 //  make file from zlist
zlist_t * zlist_from_file(ch *filename);                                         //  make zlist from file

//  random number functions =====================================================

int lrandz(int64 * seed);                                                        //  returns 0 to 0x7fffffff
int lrandz();                                                                    //  built-in seed
double drandz(int64 * seed);                                                     //  returns 0.0 to 0.99999...
double drandz();                                                                 //  built-in seed

//  spline curve-fitting functions ==============================================

void spline1(int nn, float *dx, float *dy);                                      //  define a curve using nn data points
float spline2(float x);                                                          //  return y-value for given x-value

//  FIFO queue for text strings, single or dual-thread access ===================

typedef struct  {
   int      qcap;                                                                //  queue capacity
   int      qnewest;                                                             //  newest entry position, circular
   int      qoldest;                                                             //  oldest entry position, circular
   int      qdone;                                                               //  flag, last entry added to queue
   ch       **qtext;                                                             //  up to qcap text strings
}  Qtext;

void Qtext_open(Qtext *qtext, int cap);                                          //  initialize Qtext queue, empty
void Qtext_put(Qtext *qtext, ch *format, ...);                                   //  add text string to Qtext queue
ch * Qtext_get(Qtext *qtext);                                                    //  remove text string from Qtext queue
void Qtext_close(Qtext *qtext);                                                  //  close Qtext, zfree() leftover strings

//  application initialization and administration ===============================

int zinitapp(ch *appvers, int argc, ch *argv[]);                                 //  initialize app (appname-N.N, custom homedir)

ch * get_zprefix();                                                              //  get /usr or /usr/local  ... 
ch * get_zhomedir();                                                             //  get /home/user/.appname/
ch * get_zdatadir();                                                             //  get data folder
ch * get_zimagedir();                                                            //  get image folder
ch * get_zdocdir();                                                              //  get document folder

void zabout(GtkWidget *parent);                                                  //  popup app 'about' information
void zsetfont(ch *newfont);                                                      //  set new app font and size
int  widget_font_metrics(GtkWidget *widget, int &fww, int &fhh);                 //  get widget font char width/height

int  get_zfilespec(ch *ftype, ch *fname, ch *filespec);                          //  get installation data file
void showz_logfile(GtkWidget *parent);                                           //  show log file in popup window
void showz_textfile(ch *type, ch *file, GtkWidget *parent);                      //  show text file [.gz] in popup window
void showz_docfile(GtkWidget *, ch *docfile, ch *topic);                         //  show docfile topic and assoc. image


/********************************************************************************
   GTK utility functions
*********************************************************************************/

void zmainloop(int skip = 0);                                                    //  do main loop, process menu events
void zmainsleep(float secs);                                                     //  do main loop and sleep designated time


/********************************************************************************/

//  cairo drawing region for GDK window

typedef struct {
   GdkWindow               *win;
   cairo_rectangle_int_t   rect;
   cairo_region_t          *reg;
   GdkDrawingContext       *ctx;
   cairo_t                 *dcr = 0;
}  draw_context_t;

cairo_t * draw_context_create(GdkWindow *gdkwin, draw_context_t &context);
void draw_context_destroy(draw_context_t &context);


/********************************************************************************/

//  connect KB and mouse events to widget response function

#define G_SIGNAL(widget,event,func,arg) \
        g_signal_connect(G_OBJECT(widget),event,G_CALLBACK(func),(void *) arg)


/********************************************************************************/

//  textwidget functions - scrollable text widget for text reports and line editing
//  widget = zdialog_gtkwidget(zd,textwidget) where textwidget is a zdialog "text" widget type

void textwidget_clear(GtkWidget *widget);                                              //  clear all text
void textwidget_clear(GtkWidget *widget, int line);                                    //  clear text from line to end
int  textwidget_linecount(GtkWidget *widget);                                          //  get current line count
void textwidget_append(GtkWidget *widget, int bold, ch *format, ...);                  //  append line
void textwidget_append2(GtkWidget *widget, int bold, ch *format, ...);                 //  append line and scroll to end
void textwidget_insert(GtkWidget *widget, int bold, int line, ch *format, ...);        //  insert line 
void textwidget_replace(GtkWidget *widget, int bold, int line, ch *format, ...);       //  replace line
void textwidget_delete(GtkWidget *widget, int line);                                   //  delete line
int  textwidget_find(GtkWidget *widget, ch *matchtext, int line1, int hilite);         //  find matching line
void textwidget_insert_pixbuf(GtkWidget *textwidget, int line, GdkPixbuf *pixbuf);     //  insert pixbuf image
void textwidget_scroll(GtkWidget *widget, int line);                                   //  scroll line on screen
void textwidget_scroll_top(GtkWidget *widget, int line);                               //  scroll line to top of window
void textwidget_get_visible_lines(GtkWidget *textwidget, int &top, int &bott);         //  get range of visible lines
void textwidget_dump(GtkWidget *widget, ch *filename);                                 //  dump all text into a file
void textwidget_save(GtkWidget *widget, GtkWindow *parent);                            //  same, with save-as dialog
ch * textwidget_line(GtkWidget *widget, int line, int strip);                          //  retrieve line (strip \n) 
void textwidget_highlight_line(GtkWidget *widget, int line);                           //  highlight line
ch * textwidget_word(GtkWidget *, int line, int posn, ch *dlims, ch &end);             //  retrieve word
void textwidget_highlight_word(GtkWidget *widget, int line, int posn, int cc);         //  highlight word
void textwidget_bold_word(GtkWidget *widget, int line, int posn, int cc);              //  make word bold
void textwidget_underline_word(GtkWidget *widget, int line, int posn, int cc);         //  make word underlined
void textwidget_font_attributes(GtkWidget *widget);                                    //  set font attributes for all text

typedef void textwidget_callbackfunc_t(GtkWidget *, int line, int posn, int KBkey);    //  widget event function to receive
void textwidget_set_eventfunc(GtkWidget *, textwidget_callbackfunc_t func);            //    mouse click and KB events


/********************************************************************************/

//  functions to simplify building menus, tool bars, status bars

typedef void cbFunc(GtkWidget *, ch *mname);                                     //  menu or button response function

GtkWidget * create_menubar(GtkWidget *vbox);                                     //  create menubar in packing box
GtkWidget * add_menubar_item(GtkWidget *mbar, ch *mname, cbFunc func = 0);       //  add menu item to menubar
GtkWidget * add_submenu_item(GtkWidget *mitem, ch *subname,                      //  add submenu item to menu item
                             cbFunc func = 0, ch *mtip = 0);                     //    with opt. function and popup tip

GtkWidget * create_toolbar(GtkWidget *vbox, int iconsize = 24);                  //  toolbar in packing box (no vert gtk3)
GtkWidget * add_toolbar_button(GtkWidget *tbar, ch *lab, ch *tip,                //  add button with label, tool-tip, icon
                                 ch *icon, cbFunc func);

GtkWidget * create_stbar(GtkWidget *vbox);                                       //  create status bar in packing box
int stbar_message(GtkWidget *stbar, ch *message);                                //  display message in status bar

/********************************************************************************/

GtkWidget * create_popmenu();                                                    //  create an empty popup menu
GtkWidget * add_popmenu_item(GtkWidget *popmenu, ch *mname,                      //  add menu item to popup menu
                           cbFunc func, ch *arg, ch *mtip = 0);
void popup_menu(GtkWidget *, GtkWidget *popmenu);                                //  pop-up menu at current mouse posn.


/********************************************************************************/

//  create vertical menu/toolbar in vertical packing box

struct vmenuent {                                                                //  menu data from caller
   ch          *name = 0;                                                        //  menu name, text
   ch          *icon = 0;                                                        //  opt. icon file name
   ch          *desc = 0;                                                        //  description (mouse hover popup)
   cbFunc      *LMfunc;                                                          //  menu func for left mouse click
   cbFunc      *RMfunc;                                                          //  menu func for right mouse click
   cbFunc      *setupfunc;                                                       //  opt. setup func for menu func
   ch          *arg = 0;                                                         //  callback arg for menu func
   ch          *setuparg = 0;                                                    //  callback arg for setup func
   ch          *RMarg = 0;                                                       //  callback arg for RMfunc
   PIXBUF      *pixbuf;                                                          //  icon pixbuf or null
   PangoLayout *playout1, *playout2;                                             //  normal and bold menu text
   int         namex, namey;                                                     //  menu name position in layout
   int         iconx, icony;                                                     //  menu icon position
   int         ylo, yhi;                                                         //  menu height limits
   int         iconww, iconhh;                                                   //  icon width and height
};

struct Vmenu {
   GtkWidget   *vbox;                                                            //  parent window (container)
   GtkWidget   *topwin;                                                          //  top-level window of parent
   GtkWidget   *layout;                                                          //  drawing window
   float       fgRGB[3];                                                         //  font color, RGB scaled 0-1
   float       bgRGB[3];                                                         //  background color, RGB scaled 0-1
   int         xmax, ymax;                                                       //  layout size
   int         mcount;                                                           //  menu entry count
   vmenuent    menu[100];
};

Vmenu *Vmenu_new(GtkWidget *vbox, float fgRGB[3], float bgRGB[3]);               //  create new menu in parent vbox
void Vmenu_add(Vmenu *vbm, ch *name, ch *icon,                                   //  add menu item with response function
               int iconww, int iconhh, ch *desc,                                 //  function may be popup_menu()
               cbFunc func, ch *arg);
void Vmenu_add_setup(Vmenu *vbm, int me, cbFunc RMfunc, ch *arg);                //  add opt. setup function
void Vmenu_add_RMfunc(Vmenu *vbm, int me, cbFunc RMfunc, ch *arg);               //  add function for right mouse click
void Vmenu_block(int flag);                                                      //  block or unblock menu


/********************************************************************************/

//  spline curve edit functions

typedef void spcfunc_t(int spc);                                                 //  callback function, spline curve edit

struct spldat {                                                                  //  spline curve data
   GtkWidget   *drawarea;                                                        //  drawing area for spline curves
   spcfunc_t   *spcfunc;                                                         //  callback function when curve changed
   int         Nscale;                                                           //  no. of fixed scale lines, 0-10
   float       xscale[2][10];                                                    //  2 x-values for end points
   float       yscale[2][10];                                                    //  2 y-values for end points
   int         Nspc;                                                             //  number of curves, 1-10
   int         fact[10];                                                         //  curve is active 
   int         vert[10];                                                         //  curve is vert. (1) or horz. (0)
   int         mod[10];                                                          //  curve is edited/modified 
   int         nap[10];                                                          //  anchor points per curve
   float       apx[10][50], apy[10][50];                                         //  up to 50 anchor points per curve
   float       yval[10][1000];                                                   //  y-values for x = 0 to 1 by 0.001
};

spldat * splcurve_init(GtkWidget *frame, void func(int spc));                    //  initialize spline curves
int      splcurve_adjust(void *, GdkEventButton *event, spldat *);               //  curve editing function
int      splcurve_addnode(spldat *, int spc, float px, float py);                //  add a new node to a curve
int      splcurve_resize(GtkWidget *);                                           //  adjust drawing area height
int      splcurve_draw(GtkWidget *, cairo_t *, spldat *);                        //  spline curve draw signal function
int      splcurve_generate(spldat *, int spc);                                   //  generate data from anchor points
float    splcurve_yval(spldat *, int spc, float xval);                           //  get curve y-value
int      splcurve_load(spldat *sd, FILE *fid);                                   //  load curve from a file
int      splcurve_save(spldat *sd, FILE *fid);                                   //  save curve to a file


/********************************************************************************/

//   functions to implement GTK dialogs with less complexity
//   widget types: dialog, hbox, vbox, hsep, vsep, frame, scrwin, label, link, 
//                 entry, edit, text, radio, check, button, togbutt, spin, 
//                 combo, hscale, vscale, imagebutt, colorbutt, icon, image
                                            
#define zdmaxwidgets 300
#define zdmaxbutts 10
#define zdsentinel 0x97530000
#define zdialog_max 20

struct zwidget  {
      ch          *type = 0;                       //  hbox, vbox, label, entry ...
      ch          *wname = 0;                      //  widget name
      ch          *pname = 0;                      //  parent (container) name
      ch          *data = 0;                       //  widget data, initial / returned
      int         size;                            //  text entry cc or image pixel size
      int         homog;                           //  hbox/vbox: equal spacing flag
      int         expand;                          //  widget is expandable flag
      int         space;                           //  extra padding space (pixels)
      int         wrap;                            //  wrap mode for edit widget
      int         rescale;                         //  widget is rescaled for more resolution
      double      lval, nval, hval;                //  scale range and neutral value
      double      lolim, hilim, step;              //  range and step value for number widget
      zlist_t     *zlist;                          //  combo box list of text entries 
      GtkWidget   *widget;                         //  GTK widget pointer
};

struct zdialog  {
      int         sentinel1;                       //  validity sentinel1
      int         uniqueID;                        //  unique ID, monotone increasing
      ch          *title = 0;                      //  dialog title
      void        *eventCB;                        //  dialog event user callback function
      void        *popup_report_CB;                //  callback function for popup_report
      int         zrunning;                        //  dialog is running (0,1)
      int         zstat;                           //  dialog status (from completion button)
      ch          zstat_button[40];                //  completion button label
      int         disabled;                        //  widget signals/events are disabled
      int         saveposn;                        //  save and recall window position each use
      int         saveinputs;                      //  save and recall user inputs each use
      GtkWidget   *dialog;                         //  dialog window or null (box parent)
      GtkWidget   *parent;                         //  parent window or null
      ch          *ptype = 0;                      //  null or "window" or "box" parent
      ch          *compbutton[zdmaxbutts];         //  dialog completion button labels
      GtkWidget   *compwidget[zdmaxbutts];         //  dialog completion button widgets
      zwidget     widget[zdmaxwidgets];            //  dialog widgets (EOF = type = 0)
      ch          event[40];                       //  active event or widget
      GtkWidget   *lastwidget;                     //  last widget active
      int         sentinel2;                       //  validity sentinel2
};

zdialog *zdialog_new(ch *title, GtkWidget *parent, ...);                         //  create a zdialog with opt. buttons
void zdialog_set_title(zdialog *zd, ch *title);                                  //  change zdialog title
void zdialog_set_modal(zdialog *zd);                                             //  set zdialog modal
void zdialog_set_decorated(zdialog *zd, int decorated);                          //  set zdialog decorated or not
void zdialog_present(zdialog *zd);                                               //  zdialog visible and on top
void zdialog_can_focus(zdialog *zd, int Fcan);                                   //  zdialog can/not have focus (e.g. report)
void zdialog_set_focus(zdialog *zd, ch *widget = null);                          //  set focus on window [ widget ]

int zdialog_add_widget_long(zdialog *zd,                                         //  add widget to zdialog
      ch *type, ch *wname, ch *pname,                                            //  required args
      ch *data = 0, int size = 0, int homog = 0,                                 //  optional args
      int expand = 0, int space = 0, int wrap = 0);

int zdialog_add_widget(zdialog *zd,                                              //  add widget to zdialog
      ch *type, ch *wname, ch *pname,                                            //  (alternative form)
      ch *data = 0, ch *options = 0);                                            //  "size=nn|homog|expand|space=nn|wrap"

int zdialog_valid(zdialog *zd, ch *title = 0);                                   //  return 1/0 if zdialog valid/invalid
int zdialog_valid2(zdialog *zd, ch *title = 0);                                  //  silent version of above
int zdialog_find_widget(zdialog *zd, ch *wname);                                 //  find zdialog widget from widget name
GtkWidget * zdialog_gtkwidget(zdialog *zd, ch *wname);                           //  GTK widget from zdialog widget name
int zdialog_set_image(zdialog *zd, ch *wname, GdkPixbuf *);                      //  set "image" widget from a GDK pixbuf
int zdialog_add_ttip(zdialog *zd, ch *wname, ch *ttip);                          //  add popup tool tip to a widget
int zdialog_resize(zdialog *zd, int width, int height);                          //  set size > widget sizes
int zdialog_put_data(zdialog *zd, ch *wname, ch *data);                          //  put data in widget (entry, spin ...)
ch * zdialog_get_data(zdialog *zd, ch *wname);                                   //  get widget data
int zdialog_set_limits(zdialog *, ch *wname, double min, double max);            //  set new widget limits (spin, scale)
int zdialog_get_limits(zdialog *, ch *wname, double &min, double &max);          //  get widget limits (spin, scale)
int zdialog_rescale(zdialog *zd, ch *wname, float, float, float);                //  rescale widget, lo/neut/hi vals

typedef int zdialog_event(zdialog *zd, ch *wname);                               //  widget event callback function
int zdialog_run(zdialog *zd, zdialog_event = 0, ch *posn = 0);                   //  run dialog, handle events
void zdialog_KB_addshortcut(ch *key, ch *menu);                                  //  KB shortcut for zdialog compl. button
void KBevent(GdkEventKey *event);                                                //  extern: pass KB events to main app

int zdialog_send_event(zdialog *zd, ch *event);                                  //  send an event to an active dialog
int zdialog_send_response(zdialog *zd, int zstat);                               //  complete a dialog, set status
int zdialog_show(zdialog *zd, int flag);                                         //  show or hide a dialog
int zdialog_destroy(zdialog *zd);                                                //  destroy dialog (caller resp.)
int zdialog_free(zdialog *&zd);                                                  //  free zdialog memory
int zdialog_wait(zdialog *zd);                                                   //  wait for dialog completion
int zdialog_goto(zdialog *zd, ch *wname);                                        //  put cursor at named widget
void zdialog_set_cursor(zdialog *zd, GdkCursor *cursor);                         //  set cursor for dialog window

int zdialog_stuff(zdialog *zd, ch *wname, ch *data);                             //  stuff string data into widget
int zdialog_stuff(zdialog *zd, ch *wname, int  data);                            //  stuff int data
int zdialog_stuff(zdialog *zd, ch *wname, double data);                          //  stuff double data
int zdialog_stuff(zdialog *zd, ch *wname, double data, ch *format);              //  stuff double data, formatted
int zdialog_labelfont(zdialog *zd, ch *lab, ch *font, ch *txt);                  //  stuff label text with font

int zdialog_fetch(zdialog *zd, ch *wname, ch *data, int maxcc);                  //  get string data from widget
int zdialog_fetch(zdialog *zd, ch *wname, int  &data);                           //  get int data
int zdialog_fetch(zdialog *zd, ch *wname, double &data);                         //  get double data
int zdialog_fetch(zdialog *zd, ch *wname, float &data);                          //  get float data

int zdialog_combo_clear(zdialog *zd, ch *wname);                                 //  clear combo box entries
int zdialog_combo_popup(zdialog *zd, ch *wname);                                 //  open combo box pick list

int zdialog_load_widgets(zdialog *zd, spldat *sd, ch *fname, FILE *fid);         //  load zdialog widgets from a file
int zdialog_save_widgets(zdialog *zd, spldat *sd, ch *fname, FILE *fid);         //  save zdialog widgets to a file
int zdialog_load_prev_widgets(zdialog *zd, spldat *sd, ch *fname);               //  save last-used zdialog widgets
int zdialog_save_last_widgets(zdialog *zd, spldat *sd, ch *fname);               //  load last-used zdialog widgets

int  zdialog_geometry(ch *action);                                               //  load/save zdialog window positiion/size
void zdialog_set_position(zdialog *zd, ch *posn);                                //  set initial/new zdialog window position
void zdialog_save_position(zdialog *zd);                                         //  save zdialog window position

int zdialog_inputs(ch *action);                                                  //  load or save zdialog input fields
int zdialog_save_inputs(zdialog *zd);                                            //  save zdialog input fields when finished
int zdialog_load_inputs(zdialog *zd);                                            //  restore zdialog inputs from prior use

ch * zdialog_text(GtkWidget *parent, ch *title, ch *inittext);                   //  get N text input lines from user
ch * zdialog_text1(GtkWidget *parent, ch *title, ch *inittext);                  //  get one text input line from user
ch * zdialog_password(GtkWidget *parent, ch *title, ch *inittext);               //  same, but input is replaced with '*'s
int  zdialog_choose(GtkWidget *parent, ch *where, ch *message, ...);             //  show message, return button choice  
int  zdialog_choose2(GtkWidget *parent, ch *where, ch *message, ...);            //  same, including KB inputs
int  zdialog_edit_textfile(GtkWidget *parent, char *file);                       //  popup zdialog to edit a text file

//  write text report in popup window

zdialog * popup_report_open(ch *title, GtkWidget *parent, int ww, int hh,        //  open popup report - pixel size,
               int wrap, int Fheader, textwidget_callbackfunc_t CBfunc, ...);    //    wrap, header line, callback function
void popup_report_header(zdialog *zd, int bold, ch *format, ...);                //  write non-scrolling header line
void popup_report_write(zdialog *zd, int bold, ch *format, ...);                 //  write text line
void popup_report_write2(zdialog *zd, int bold, ch *format, ...);                //  write text line and scroll to end
void popup_report_top(zdialog *zd);                                              //  go to top of report window
void popup_report_bottom(zdialog *zd);                                           //  go to bottom of report window
void popup_report_clear(zdialog *zd);                                            //  clear report window
void popup_report_clear(zdialog *zd, int line);                                  //  clear from line to end
void popup_report_insert(zdialog *zd, int bold, int line, ch *format, ...);      //  insert new line
void popup_report_replace(zdialog *zd, int bold, int line, ch *format, ...);     //  replace existing line
void popup_report_delete(zdialog *zd, int line);                                 //  delete line
int  popup_report_find(zdialog *zd, ch *matchtext, int line1);                   //  find matching line
void popup_report_insert_pixbuf(zdialog *zd, int line, GdkPixbuf *pixbuf);       //  insert pixbuf image after line
void popup_report_scroll(zdialog *zd, int line);                                 //  scroll to make line visible
void popup_report_scroll_top(zdialog *zd, int line);                             //  scroll to put line at top
void popup_report_get_visible_lines(zdialog *zd, int &top, int &bott);           //  get visible lines range
ch * popup_report_line(zdialog *zd, int line, int strip);                        //  retrieve line (strip \n) 
ch * popup_report_word(zdialog *zd, int line, int posn, ch *dlims, ch &end);     //  retrieve word
void popup_report_highlight_line(zdialog *zd, int line);                         //  highlight line
void popup_report_highlight_word(zdialog *zd, int line, int posn, int cc);       //  highlight word
void popup_report_underline_word(zdialog *zd, int line, int posn, int cc);       //  underline word 
void popup_report_bold_word(zdialog *zd, int line, int posn, int cc);            //  bold word
void popup_report_font_attributes(zdialog *zd);                                  //  font attributes for entire report
void popup_report_close(zdialog *zd, int secs);                                  //  close window after seconds

//  shell command to popup window

int popup_command(ch *command, int ww = 400, int hh = 300, GtkWidget *parent = 0, int top = 0);

//  popups: message, dialog, image, picklist

void zmessageACK(GtkWidget *parent, ch *format, ... );                           //  display message, wait for OK
int  zmessageYN(GtkWidget *parent, ch *format, ... );                            //  display message, wait for YES/NO
zdialog * zmessage_post(GtkWidget *, ch *loc, int s, ch *f, ...);                //  show message, timeout or cancel

void poptext_screen(ch *text, int px, int py, float s1, float s2);               //  show popup text at screen posn
void poptext_mouse(ch *text, int dx, int dy, float s1, float s2);                //  same, at mouse posn + offset
void poptext_window(GtkWindow *, ch *tx, int x, int y, float s1, float s2);      //  same, at window posn + offset
void poptext_widget(GtkWidget *, ch *tx, int x, int y, float s1, float s2);      //  same, at widget posn + offset
int  poptext_killnow();                                                          //  kill current popup window
ch * popup_picklist(GtkWidget *paren, ch **list, ch **desc, int Np);             //  popup picklist, return choice

//  file chooser dialogs for one file, multiple files, or folder

ch  * zgetfile(ch *title, GtkWindow *parent, ch *action, ch *file, int hidden = 0);
ch ** zgetfiles(ch *title, GtkWindow *parent, ch *action, ch *file, int hidden = 0);
ch  * zgetfolder(ch *title, GtkWindow *parent, ch *initfolder);

//  print an image file, choosing printer, paper, orientation, margins, and scale

void print_image_file(GtkWidget *parent, ch *imagefile);

//  drag and drop functions

typedef ch * drag_drop_source_func();                                            //  user function, set drag-drop text
typedef void drag_drop_dest_func(int x, int y, ch *text);                        //  user function, get drag-drop text
void drag_drop_source(GtkWidget *window, drag_drop_source_func);                 //  connect source window to user function
void drag_drop_dest(GtkWidget *window, drag_drop_dest_func);                     //  connect dest. window to user function

//  miscellaneous GDK/GTK functions

PIXBUF * get_thumbnail(ch *fpath, int size);                                     //  get sized thumbnail for image file
GdkCursor * zmakecursor(ch *iconfile);                                           //  make a cursor from an image file
PIXBUF * gdk_pixbuf_rotate(PIXBUF *, float deg, int alfa = 0);                   //  rotate pixbuf through any angle
PIXBUF * gdk_pixbuf_stripalpha(PIXBUF *pixbuf);                                  //  strip alpha channel from pixbuf
PIXBUF * text_pixbuf(ch *text, ch *font, int fsize, GtkWidget *);                //  create pixbuf with text using font

int  move_pointer(GtkWidget *, int px, int py);                                  //  move the mouse pointer to px, py
void window_to_mouse(GtkWidget *window);                                         //  move GtkWidget/window to mouse position


