/* utils.c
 * Functions useful across the different modules of
 * drawing and non-drawing code.
 *
 * for Denemo, a gtk+ frontend to GNU Lilypond
 * (c) 1999-2005 Matthew Hiller
 */

#include <stdio.h>
#include <string.h>             /*for SIGTERM */
#include <ctype.h>
#include <math.h>
#include <fontconfig/fontconfig.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include <glib/gstdio.h> //for g_remove. Use GFile instead?
#include <stdlib.h>
#include "display/accwidths.h"
#include <denemo/denemo.h>
#include "display/notewidths.h"
#include "core/utils.h"
#include "smf.h"
#include "export/print.h"
#include "core/kbd-custom.h"
#include "core/view.h"
#include "core/menusystem.h"
#include "command/lilydirectives.h"
#include "command/object.h"
#include "command/scorelayout.h"
#include "printview/markupview.h"
#include <signal.h>             /*for SIGTERM */

#include "config.h"
#ifdef G_OS_WIN32
#include "windows.h"
#else
#include "core/binreloc.h"
#endif
#include "audio/pitchentry.h"
#include "command/measure.h"
#ifdef _MACH_O_
#include <mach-o/dyld.h>
#endif

//generated by cairo_svg2path from piano_staff.svg
static cairo_path_data_t piano_brace_data[] = {
  {.header.type = 0,.header.length = 2},
  {.point.x = 13.694762,.point.y = 15.134294},
  {.header.type = 0,.header.length = 2},
  {.point.x = 13.694762,.point.y = 34.647228},
  {.header.type = 2,.header.length = 4},
  {.point.x = 13.694745,.point.y = 37.873117},
  {.point.x = 13.456160,.point.y = 40.450299},
  {.point.x = 12.979007,.point.y = 42.378764},
  {.header.type = 2,.header.length = 4},
  {.point.x = 12.533633,.point.y = 44.307289},
  {.point.x = 11.849690,.point.y = 45.797498},
  {.point.x = 10.927179,.point.y = 46.849380},
  {.header.type = 2,.header.length = 4},
  {.point.x = 10.036446,.point.y = 47.901323},
  {.point.x = 8.748092,.point.y = 48.830499},
  {.point.x = 7.062105,.point.y = 49.636952},
  {.header.type = 2,.header.length = 4},
  {.point.x = 9.734241,.point.y = 50.899267},
  {.point.x = 11.499767,.point.y = 52.722581},
  {.point.x = 12.358688,.point.y = 55.106874},
  {.header.type = 2,.header.length = 4},
  {.point.x = 13.249387,.point.y = 57.491228},
  {.point.x = 13.694745,.point.y = 61.102772},
  {.point.x = 13.694762,.point.y = 65.941541},
  {.header.type = 1,.header.length = 2},
  {.point.x = 13.694762,.point.y = 83.666226},
  {.header.type = 2,.header.length = 4},
  {.point.x = 13.694745,.point.y = 88.119297},
  {.point.x = 14.251441,.point.y = 91.450330},
  {.point.x = 15.364854,.point.y = 93.659376},
  {.header.type = 2,.header.length = 4},
  {.point.x = 15.937439,.point.y = 94.781380},
  {.point.x = 16.557760,.point.y = 95.675508},
  {.point.x = 17.225817,.point.y = 96.341727},
  {.header.type = 2,.header.length = 4},
  {.point.x = 17.925643,.point.y = 97.042993},
  {.point.x = 18.434624,.point.y = 97.463756},
  {.point.x = 18.752761,.point.y = 97.604034},
  {.header.type = 2,.header.length = 4},
  {.point.x = 19.102663,.point.y = 97.744242},
  {.point.x = 19.532113,.point.y = 97.884511},
  {.point.x = 20.041117,.point.y = 98.024788},
  {.header.type = 1,.header.length = 2},
  {.point.x = 20.041117,.point.y = 99.602655},
  {.header.type = 2,.header.length = 4},
  {.point.x = 14.442310,.point.y = 99.427312},
  {.point.x = 10.815824,.point.y = 98.112447},
  {.point.x = 9.161650,.point.y = 95.658006},
  {.header.type = 2,.header.length = 4},
  {.point.x = 7.412016,.point.y = 93.028214},
  {.point.x = 6.537207,.point.y = 88.487468},
  {.point.x = 6.537217,.point.y = 82.035749},
  {.header.type = 1,.header.length = 2},
  {.point.x = 6.537217,.point.y = 63.206580},
  {.header.type = 2,.header.length = 4},
  {.point.x = 6.537207,.point.y = 60.787187},
  {.point.x = 6.362244,.point.y = 58.735972},
  {.point.x = 6.012330,.point.y = 57.052911},
  {.header.type = 2,.header.length = 4},
  {.point.x = 5.694207,.point.y = 55.369866},
  {.point.x = 5.026171,.point.y = 53.879675},
  {.point.x = 4.008217,.point.y = 52.582286},
  {.header.type = 2,.header.length = 4},
  {.point.x = 3.022060,.point.y = 51.249883},
  {.point.x = 1.685986,.point.y = 50.373300},
  {.point.x = -0.000008,.point.y = 49.952520},
  {.header.type = 1,.header.length = 2},
  {.point.x = -0.000008,.point.y = 49.373968},
  {.header.type = 2,.header.length = 4},
  {.point.x = 4.358134,.point.y = 48.006509},
  {.point.x = 6.537207,.point.y = 43.676162},
  {.point.x = 6.537217,.point.y = 36.382874},
  {.header.type = 1,.header.length = 2},
  {.point.x = 6.537217,.point.y = 15.975830},
  {.header.type = 2,.header.length = 4},
  {.point.x = 6.537207,.point.y = 10.120270},
  {.point.x = 7.443829,.point.y = 5.965213},
  {.point.x = 9.257083,.point.y = 3.510685},
  {.header.type = 2,.header.length = 4},
  {.point.x = 11.102126,.point.y = 1.056314},
  {.point.x = 14.696800,.point.y = -0.258578},
  {.point.x = 20.041117,.point.y = -0.433981},
  {.header.type = 1,.header.length = 2},
  {.point.x = 20.041117,.point.y = 1.143903},
  {.header.type = 2,.header.length = 4},
  {.point.x = 18.068795,.point.y = 1.740052},
  {.point.x = 16.462326,.point.y = 3.212732},
  {.point.x = 15.221705,.point.y = 5.561917},
  {.header.type = 2,.header.length = 4},
  {.point.x = 14.203726,.point.y = 7.490478},
  {.point.x = 13.694745,.point.y = 10.681285},
  {.point.x = 13.694762,.point.y = 15.134294},

};

static cairo_path_t piano_brace_path = { 0, piano_brace_data, 92 };

gboolean compare_gstring (GString *g1, GString *g2)
{
  if (g1 && g2 && !strcmp (g1->str, g2->str))
    return TRUE;
  return g1==g2;
}

gboolean compare_glists (GList *l1, GList *l2)
{
  while (l1 && l2)
    {
      if (l1->data!=l2->data)
        return FALSE;
      l1=l1->next;
      l2=l2->next;
    }
  return l1 == l2;
}

/**
 * This checks to see if there's a .denemo/ directory in the user's
 * home directory,
 * if create tries to create one if there isn't, and returns the
 * path to it
 * else returns NULL
 *
 * .denemo/ is used for holding configuration files, templates, and so on.
 *
 * On windows the home directory is the one containing the My Documents folder.
 */

const gchar *
get_user_data_dir (gboolean create)
{
  static gchar *dotdenemo = NULL;

  gboolean err;
  if (!dotdenemo)
    {
      dotdenemo = g_build_filename (g_get_home_dir (), ".denemo-" PACKAGE_VERSION, NULL);
    }
  if ((!create) && !g_file_test (dotdenemo, G_FILE_TEST_IS_DIR))
    return NULL;
  err = g_mkdir_with_parents (dotdenemo, 0770);
  if (err)
    {
      warningdialog (_("Could not create .denemo for you personal settings"));
      g_free (dotdenemo);
      dotdenemo = NULL;
    }
  return dotdenemo;
}

void localization_init (void)
{
  setlocale (LC_ALL, "");
  bindtextdomain(GETTEXT_PACKAGE, get_system_locale_dir ());
  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
  textdomain(GETTEXT_PACKAGE);
}


// Create or remove a unique temporary directory
// If removal is FALSE, the directory will be newly
// created or the existing temporary directory will
// be returned.
// If removal is TRUE, the directory gets removed and NULL is returned.
gchar *make_temp_dir (gboolean removal)
{
  static gchar *tmpdir = NULL;
  if (!removal)
    {
      // Either create a new directory or get the path
      if (!tmpdir)
        {
          gchar *newdir = g_build_filename (g_get_tmp_dir (), "Denemo_XXXXXX", NULL);
#ifdef G_OS_WIN32
          // Windows does not delete the temporary directory, use a constant one.
          if (g_mkdir_with_parents (newdir, 0700))
            g_warning ("Creation of temp dir %s failed\n", newdir);
#else
          if (!g_mkdtemp (newdir))
            g_warning ("Creation of temp dir %s failed\n", newdir);
#endif
          tmpdir = newdir;
        }
    }
  else
    {
      // The directory should be removed.
      // Remove all files in the directory before deleting it.
      GError *error = NULL;
      GDir *dir = g_dir_open (tmpdir, 0, &error);
      if (!error)
        {
          const gchar *filename;
          while (filename = g_dir_read_name (dir))
            {
              gchar *fullpath = g_build_filename (tmpdir, filename, NULL);
              g_remove (fullpath);
              g_free (fullpath);
            }
          g_dir_close (dir);
        }
      g_remove (tmpdir);
      tmpdir = NULL;
    }
    return tmpdir;
}

// Return a path to a temporary directory to be used for print intermediate files
const gchar *
locateprintdir (void)
{
  return make_temp_dir (FALSE);
}

// Remove the temporary directory
void
removeprintdir (void)
{
  make_temp_dir (TRUE);
}

void copy_file (gchar *source_file, gchar *dest_file)
{
	GError *error = NULL;
	gchar *contents;
	gsize length;
	if (g_file_get_contents (source_file, &contents, &length, &error))
		 g_file_set_contents (dest_file, contents, length, &error);
	if (error)
		g_warning ("Failed to copy file %s to %s message: %s\n", source_file, dest_file, error->message);
	g_free (contents);
}
//copies all files in source_dir to dest_dir creating the latter if need be
void copy_files (gchar *source_dir, gchar *dest_dir)
{
 GError *error = NULL;
 GDir *thedir;
 const gchar *thefile;
 if (-1 == g_mkdir_with_parents (dest_dir, 0770))
    {
        g_warning ("Could not create %s\n", dest_dir);
        return;
    }
 thedir = g_dir_open (source_dir, 0, &error);
 if (error)
    {
        g_warning ("Could not open %s\n", source_dir);
        return;
    }
 if (thedir)
    {
    while ((thefile = g_dir_read_name (thedir)) != NULL)
        {
		gchar *path = g_build_filename (source_dir, thefile, NULL);
		gchar *newfile = g_build_filename (dest_dir, thefile, NULL);
		//g_print ("Testing is directory? %s\n\n\n", path);
			if (g_file_test (path, G_FILE_TEST_IS_DIR))
				{
					
					g_mkdir_with_parents (newfile, 0770);
					copy_files (path, newfile);
				}
			else {
					copy_file (path, newfile);
					
	
				}
		g_free (newfile);
		g_free (path);
		}
    g_dir_close (thedir);
   }
}

void
add_font_directory (gchar * fontpath)
{
#ifdef G_OS_WIN32
  if (0 == AddFontResource (fontpath))
    g_warning ("Failed to add font dir %s.", fontpath);
  SendMessage (HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
#endif
  if (FcConfigAppFontAddDir (NULL, (FcChar8 *) fontpath) == FcFalse)
    g_warning ("Failed to add font dir %s.", fontpath);
}

void
add_font_file (gchar * fontname)
{
#ifdef G_OS_WIN32
  if (0 == AddFontResource (fontname))
    g_warning ("Failed to add font file %s.", fontname);
  SendMessage (HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
#endif
  if (FcConfigAppFontAddFile (NULL, (FcChar8 *) fontname) == FcFalse)
    g_warning ("Failed to add font file %s.", fontname);
}

#ifdef G_OS_WIN32
gboolean CoInitializeExCalled = FALSE;
#endif

gboolean
run_file_association (gchar * filename)
{
#ifdef G_OS_WIN32
  gint value = 0;
  if (!CoInitializeExCalled)
    {
      value = CoInitializeExCalled = TRUE;
      CoInitializeEx (NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
      g_debug ("coinit returned %d\n", value);
    }
  g_info ("Running ShellExecute %s \n", filename);
  return ShellExecute (NULL, NULL, filename, NULL, NULL, 0) > 32 /* value above 32 indicating success */ ;
#else
  g_warning ("No file assoc code - set pref in externals tab of prefs dialog");
  return 0;
#endif

}

/**
   Popups up the menu named.
 */
void
popup_menu (gchar * name)
{
  GtkWidget *menu = denemo_menusystem_get_widget (name);
  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
}

/**
 * outputs a warning and sounds bell
 * @return none
 */
void
warningmessage (gchar * msg)
{
  gdk_beep ();
  g_warning ("%s", msg);
}

/**
 * Pops up an info or warning dialog and blocks until it is dismissed
 *  @param msg warning message to display
 *  @param info TRUE if informational only
 * @return none
 */
void
infowarningdialog (gchar * msg, gboolean info)
{
  if (Denemo.non_interactive)
    g_warning ("%s", msg);
  else
    {
      GtkWidget *dialog;
      dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), GTK_DIALOG_DESTROY_WITH_PARENT, info ? GTK_MESSAGE_INFO : GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "%s", msg);
      gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
      gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (Denemo.window));
      gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
    }
}

/**
 * Pops up a warning dialog and blocks until it is dismissed
 *  @param msg warning message to display
 * @return none
 */
void
warningdialog (gchar * msg)
{
  infowarningdialog (msg, FALSE);
}

/**
 * Displays information message to screen, not blocking.
 * User can destroy window when no longer needed.
 * @param msg message to display
 * @return dialog
 */
GtkWidget *
infodialog (gchar * msg)
{
  if (Denemo.non_interactive)
    {
      g_info ("%s", msg);
      return NULL;
    }

  GtkWidget *dialog;
  dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", msg);
#ifdef G_OS_WIN32
  gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); //needed on windows because of a bug, not all text can be seen.
#endif
  g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_hide), dialog);
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (Denemo.window));
  gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
  gtk_widget_show_all (dialog);
  return dialog;
}

/* data stucture to contain Progressbar
 * data
 */
typedef struct _ProgressData
{
  GtkWidget *window;
  GtkWidget *pbar;
  int timer;
  gboolean progressing;
} ProgressData;

static ProgressData progress_data;


/* Update the value of the progress bar so that we get
 * some movement */
static gboolean
progress_timeout (void)
{
  if (progress_data.progressing)
    gtk_progress_bar_pulse (GTK_PROGRESS_BAR (progress_data.pbar));
  else
    {
      gtk_widget_hide (progress_data.window);
      progress_data.timer = 0;
      return FALSE;
    }
  return TRUE;
}




/**
 * Displays progress bar
 * optionally pass a callback to be run on delete signal
 * @param msg message to display, callback (can be NULL)
 */
GtkWindow *
progressbar (gchar * msg, gpointer callback)
{
  if (Denemo.non_interactive)
    return NULL;
  GtkWidget *vbox;
  ProgressData *pdata = &progress_data;
  if (pdata->progressing)
    pdata->window;
  if (pdata->window == NULL)
    {
      pdata->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      gtk_window_set_accept_focus (GTK_WINDOW (pdata->window), FALSE);  //FIXME this is only a hint; perhaps we should embed the progress bar in the status line...
      gtk_window_set_title (GTK_WINDOW (pdata->window), _("Progress"));
      if (callback) //FIXME eventually we may want to pass a tooltip in to this function.
		gtk_widget_set_tooltip_text (pdata->window, _("This indicates the the LilyPond typesetter is still working on setting the Denemo score. This can take a long time, particularly for polyphony where voices must not collide. You can continue editing while the typesetting is taking place.\nKill this window if you want to re-start the typesetting e.g. after fixing a mistake you just spotted."));
      gtk_window_set_transient_for (GTK_WINDOW (pdata->window), GTK_WINDOW (Denemo.window));
      gtk_window_set_keep_above (GTK_WINDOW (pdata->window), TRUE);
      vbox = gtk_vbox_new (FALSE, 5);
      gtk_container_add (GTK_CONTAINER (pdata->window), vbox);
      pdata->pbar = gtk_progress_bar_new ();
      gtk_container_add (GTK_CONTAINER (vbox), pdata->pbar);
      gtk_widget_show_all (vbox);
    }
  /* set text inside progress bar */
  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR (pdata->pbar), TRUE);
  gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pdata->pbar), msg);
  if (pdata->timer == 0)
    pdata->timer = g_timeout_add (100, (GSourceFunc) progress_timeout, pdata);
  pdata->progressing = TRUE;    /* If this is false the progress bar will stop */
  gtk_widget_show (pdata->window);
  /* If widget is destroyed stop the printing */
  if (callback)
    g_signal_connect (G_OBJECT (pdata->window), "delete-event", G_CALLBACK (callback /*call_stop_lilypond */ ), NULL);
  else
    g_signal_connect (G_OBJECT (pdata->window), "delete-event", G_CALLBACK (progressbar_stop), NULL);
  return GTK_WINDOW (pdata->window);
}
void
progressbar_stop (void)
{
  progress_data.progressing = FALSE;
}

void keep_alive (void)
	{
		while (gtk_events_pending ())
		gtk_main_iteration ();
	}

void
busy_cursor (GtkWidget * area)
{
  static GdkCursor *busycursor = NULL;
  if (!busycursor)
    busycursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_WATCH);
  if (gtk_widget_get_window (Denemo.printarea))
    gdk_window_set_cursor (gtk_widget_get_window (area), busycursor);
}

void
normal_cursor (GtkWidget * area)
{
  static GdkCursor *arrowcursor = NULL;
  if (!arrowcursor)
    arrowcursor = Denemo.GDK_LEFT_PTR;
  if (gtk_widget_get_window (area))
    gdk_window_set_cursor (gtk_widget_get_window (area), arrowcursor);
}


/**
 *  Draws the given bitmap mask on to the pixmap using the given
 *  grahpics context.
 *
 * @param pixmap pixmap be drawn on.
 * @param gc graphics context to use
 * @param mask  bitmap to be drawn
 * @param x x position on the pixmap
 * @param y y position on the pixmap
 * @param width width of the bitmap mask
 * @param height height of the bitmap mask
 *
 * @return none
 */


#ifdef G_OS_WIN32
//this code actually works on GNU/Linux too, it is not clear what to prefer
//well it turns out glyphs from the Denemo font fail to show on Windows so we should use this
static void
windows_draw_text (cairo_t * cr, const char *font, const char *text, double x, double y, double size, gboolean invert)
{
  y -= size;
  size *= 0.75;
  PangoLayout *layout;
  PangoFontDescription *desc;
  /* Create a PangoLayout, set the font and text */
  layout = pango_cairo_create_layout (cr);

  pango_layout_set_text (layout, text, -1);
  desc = pango_font_description_from_string (font);
  pango_font_description_set_size (desc, size * PANGO_SCALE);
  pango_layout_set_font_description (layout, desc);
  pango_font_description_free (desc);
  pango_cairo_update_layout (cr, layout);


  cairo_move_to (cr, x, y);
  if (invert)
    cairo_scale (cr, 1, -1);
  pango_cairo_show_layout (cr, layout);
  /* free the layout object */
  g_object_unref (layout);
}
#endif

void
drawbitmapinverse_cr (cairo_t * cr, DenemoGraphic * mask, gint x, gint y, gboolean invert)
{
  cairo_save (cr);
  switch (mask->type)
    {
    case DENEMO_BITMAP:
      {
#if GTK_MAJOR_VERSION==3
        gdk_cairo_set_source_window (cr, mask->graphic, x, y);  //??? bitmap???? asks torbenh
#else
        cairo_rectangle (cr, x, y, mask->width, mask->height);
#endif
        cairo_fill (cr);
        break;
      }
    case DENEMO_PATTERN:
      {
        cairo_pattern_t *pattern = (cairo_pattern_t *) mask->graphic;
        cairo_translate (cr, x, y);
        cairo_mask (cr, pattern);
        break;
      }
    case DENEMO_FONT:
      {
        DenemoGlyph *glyph = mask->graphic;
#ifdef G_OS_WIN32
        windows_draw_text (cr, glyph->fontname, glyph->utf, x, y - 11, glyph->size, invert);
#else
        cairo_select_font_face (cr, glyph->fontname, glyph->slant, glyph->weight);
        cairo_set_font_size (cr, glyph->size);
        cairo_move_to (cr, x, y);

        if (invert)
          cairo_scale (cr, 1, -1);
        cairo_show_text (cr, glyph->utf);
#endif
        break;
      }
    }
  cairo_restore (cr);
}

void
drawfetachar_cr (cairo_t * cr, gunichar uc, double x, double y)
{
  int len;
  char utf_string[8];
  len = g_unichar_to_utf8 (uc, utf_string);
  utf_string[len] = '\0';
  //    windows_draw_text (cr, "feta26", utf_string, x, y, 35.0, FALSE); this fails to position stuff correctly, but the code below is working on windows anyway.
  cairo_select_font_face (cr, "feta26", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size (cr, 35.0);
  cairo_move_to (cr, x, y);
  cairo_show_text (cr, utf_string);

}


void
drawtext_cr (cairo_t * cr, const char *text, double x, double y, double size)
{
  if (*text)
    {
#ifdef OBSOLETE_G_OS_WIN32
      return windows_draw_text (cr, "Denemo", text, x, y, size, FALSE); //these values arrived at by trial and error, to match the previously used code below
#else
      //use the FreeSerif font as it has music symbols - there is no font substitution done by cairo here
      cairo_select_font_face (cr, "Denemo", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
      cairo_set_font_size (cr, size);
      cairo_move_to (cr, x, y);
      cairo_show_text (cr, text);
#endif
    }
}

void
drawnormaltext_cr (cairo_t * cr, const char *text, double x, double y)
{
  drawtext_cr (cr, text, x, y, 14.0 * (Denemo.prefs.fontsize/9.0));
}

void
drawlargetext_cr (cairo_t * cr, const char *text, double x, double y)
{
  drawtext_cr (cr, text, x, y, 24.0 * (Denemo.prefs.fontsize/9.0));
}

void general_draw_text (cairo_t * cr, const char *font, const char *text, double x, double y)
{
  PangoLayout *layout;
  PangoFontDescription *desc;
  /* Create a PangoLayout, set the font and text */
  layout = pango_cairo_create_layout (cr);
  pango_layout_set_text (layout, text, -1);
  desc = pango_font_description_from_string (font);
  pango_layout_set_font_description (layout, desc);
  pango_font_description_free (desc);
  pango_cairo_update_layout (cr, layout);
  cairo_move_to (cr, x, y);
  pango_cairo_show_layout (cr, layout);
  g_object_unref (layout);
}


/* draw display text and or graphics for directives
 return the widest graphic width*/
gint
draw_for_directives (cairo_t * cr, GList * directives, gint x, gint y, gboolean at_cursor)
{
  gint count = 10;
  gint maxwidth = 0;
  for (; directives; directives = directives->next, count += 10)
    {
      DenemoDirective *directive = (DenemoDirective *) directives->data;
      guint layout = selected_layout_id ();
      gdouble only = (directive->layouts && !wrong_layout (directive, layout)) ? 0.5 : 0.0;
      gdouble exclude = (directive->layouts && wrong_layout (directive, layout)) ? 0.9 : 0.0;
      //if (directive->y && directive->y != layout)
      //  exclude = 0.9;
      if (exclude > 0.0 || only > 0.0)
        {
          cairo_save (cr);
          cairo_set_source_rgba (cr, 0.4 + exclude - only / 2, 0.5 + only, 0.4 - only / 2, at_cursor ? 1.0 : 0.7);
        }
      if (directive->graphic)
        {
          gint gwidth, gheight;
          gwidth = directive->graphic->width;
          gheight = directive->graphic->height;

          maxwidth = MAX (gwidth, maxwidth);
          // g_print("%p %p drawing a graphic at %d %d\n",directive,Denemo.project->movement->directive_on_clipboard,   x + directive->gx + count - gwidth / 2, y + directive->gy - gheight / 2);
          drawbitmapinverse_cr (cr, directive->graphic, x + directive->gx + count - gwidth / 2, y + directive->gy - gheight / 2, FALSE);

          if (directive == Denemo.project->movement->directive_on_clipboard)
            {
              cairo_save (cr);
              cairo_set_source_rgba (cr, 0.4, 0.8, 0.5, 0.7);
              cairo_arc (cr, x + directive->gx + count, y + directive->gy, MAX (gwidth, 8.0), 0.0, 2 * M_PI);
              cairo_fill (cr);
              cairo_restore (cr);
            }



        }
      if (directive->display)
        {
#define MAXLEN (8)
          gchar c = 0;          //if it is a long string only show it all when cursor is on it also only display from first line
          gchar *p;
          for (p = directive->display->str; *p; p++)
            {
              if (*p == '\n' || (!at_cursor && (p - directive->display->str) > MAXLEN))
                {
                  c = *p;
                  *p = 0;
                  break;
                }
            }
          drawnormaltext_cr (cr, directive->display->str, x + directive->tx + count, y + directive->ty);
          if (directive == Denemo.project->movement->directive_on_clipboard)
            {
              cairo_save (cr);
              cairo_set_source_rgba (cr, 0.4, 0.8, 0.5, 0.7);
              cairo_arc (cr, x + directive->tx + count + 4, y + directive->ty - 4, 8.0, 0.0, 2 * M_PI);
              cairo_fill (cr);
              cairo_restore (cr);
            }





          if (c)
            {
              *p = c;
            }
          if (exclude > 0.0 || only > 0.0)
            cairo_restore (cr);
        }
    }

  return maxwidth;
}

/* draw a brace straight or curly */
void
draw_staff_brace (cairo_t * cr, gboolean curly, gint x, gint y, gint height)
{
    if(Denemo.hovering_over_brace)
        cairo_set_source_rgb (cr, 0, 0.8, 0.1);
    else
        cairo_set_source_rgb (cr, 0, 0, 0);
  if (!curly)
    {
      drawfetachar_cr (cr, 0xD8, x, y);
      cairo_rectangle (cr, x, y + 2.0, 3, height - 4.0);
      cairo_fill (cr);
      cairo_rectangle (cr, x + 5.0, y + 1.0, 1, height - 2.0);
      cairo_fill (cr);
      drawfetachar_cr (cr, 0xD9, x, y + height);
    }
  else
    {
      //cairo_translate (cr, 0, 10.0);
      cairo_translate (cr, x - 5, y + 2);
      cairo_scale (cr, 1.0, height / 100.0);
      cairo_append_path (cr, &piano_brace_path);
      cairo_fill (cr);
    }
  return;
}

gchar *
pretty_name (gchar * lilynote)  //display 𝄪𝄫♯♭♮ with note name
{
  static gchar *natural = NULL;
  static gchar *sharp;
  static gchar *flat;
  static gchar *double_sharp;
  static gchar *double_flat;
  gchar *answer;
  if (!natural)
    {
      natural = g_strdup ("C♮");
      sharp = g_strdup ("C♯");
      flat = g_strdup ("C♭");
      double_sharp = g_strdup ("C𝄪");
      double_flat = g_strdup ("C𝄫");
    }
  answer = natural;
  if (*(lilynote + 1) == 0)
    answer = natural;
  else if (*(lilynote + 1) == 'i')
    {
      if (*(lilynote + 3) == 'i')
        answer = double_sharp;
      else
        answer = sharp;
    }
  else if (*(lilynote + 1) == 'e')
    {
      if (*(lilynote + 3) == 'e')
        answer = double_flat;
      else
        answer = flat;
    }
  *answer = toupper (*lilynote);
  return answer;
}


/**
 * Utility function to set the number of ticks used by the given object
 * if it is within a given tuplet
 *
 * @param theobj DenemoObject to set the number of ticks
 * @param numerator numerator of the current tuplet
 * @param denominator denominator of the current tuplet
 * @return none
 */
void
set_tuplefied_numticks (DenemoObject * theobj, gint numerator, gint denominator)
{
  theobj->durinticks = theobj->basic_durinticks * numerator / denominator;
  /* Though WHOLENUMTICKS is chosen strategically so that in common
   * cases, this division works out evenly, things should also work
   * out properly if it doesn't; i.e., durinticks_untupletized is
   * rounded down */
}

/**
 * Utility function to set the number of ticks of a mudela object
 * in a grace note
 * @param theobj the DenemoObject to set the number of ticks
 * @param multiplier the grace notes multiplier
 * @return none
 */
void
set_grace_numticks (DenemoObject * theobj, gint multiplier)
{

  theobj->durinticks = theobj->basic_durinticks / multiplier;


}


/**
 * Sets the number of ticks taken by the given DenemoObject.
 *
 * @param theobj the mudela object to set the number of ticks on
 * @return none
 */
void
set_basic_numticks (DenemoObject * theobj)
{
  gint power;
  gint withoutdots;
  gint addperdot, i;

  switch (theobj->type)
    {
    case CHORD:
		{
	     gint basedur = ((chord *) theobj->object)->baseduration;
		  if ((basedur < 0) || (basedur > 7)) //not a regular 0-7 note
			{
			  if ((basedur >= -7) && (basedur < 0)) //-1 to -7 are regular (not breve etc) notes with custom duration
				{
				  g_warning ("Attempt to change duration of a custom-duration chord");
				  return;//this object has had its duration fixed
				}
			  //the high value ticks are breve, longa, maxima
			  withoutdots = basedur>0 ? basedur : -basedur; //guard against them being set to high +ve value rather than the -ve convention documented.
			}
		  else //1-7 are the regular notes wholenote downwards
			{
			  power = 1 << basedur;
			  withoutdots = WHOLE_NUMTICKS / power;
			}
		  addperdot = withoutdots / 2;
		  // basic_durinticks includes dots but excludes effects of tuplets or grace notes - see denemo_types.h
		  theobj->durinticks = theobj->basic_durinticks = withoutdots;
		  for (i = 0; i < ((chord *) theobj->object)->numdots; addperdot /= 2, i++)
			  theobj->basic_durinticks += addperdot;
		  theobj->durinticks = theobj->basic_durinticks;   
			  
		  if (((chord *) theobj->object)->is_grace)
			theobj->durinticks = 0; //durinticks takes account of grace note status
       }
      break;
    default:
      theobj->basic_durinticks = 0;
      theobj->durinticks = 0;
      /* There's no reason not to set that as well */
      break;
    }
}

/**
 * Returns the amount of space to be left after a note or rest, only
 * taking the width of the measure into consideration, just to choose a position for the cursor when appending after an object.Only called by draw.c should be moved FIXME.
 *
 * @param numticks the number of ticks taken so far
 * @param wholenotewidth the number of ticks taken be a whole note
 * @return the amount of space to be left after a note or rest
 */

gint
space_after (gint numticks, gint wholenotewidth)
{
  return MAX (numticks * wholenotewidth / WHOLE_NUMTICKS, 0);
}

#define EXTRAWIDTH 5

/**
 * Sets the minimum space that needs to be allocated for drawing a mudela
 * object based on the type
 * also sets space_before
 * @param theobj the DenemoObject to set the minimum space on
 * @return none
 */

void
setpixelmin (DenemoObject * theobj)
{
  gint i, baseduration, headtype, resttype;
  chord chordval;
  GList *tnode;
  note *thetone;
  /* And these static declaration are copied right out of drawnotes.c
   * and drawaccidentals.c */

  switch (theobj->type)
    {
    case CHORD:
      chordval = *(chord *) theobj->object;
      baseduration = chordval.baseduration;
      baseduration = MAX (baseduration, 0);
      headtype = MIN (baseduration, 2);
      resttype = MIN (baseduration, SMALLESTDURATION);
      if (resttype <0)
		resttype = 0;
      if (headtype < 0)
        headtype = 0;           //-ve values of baseduration are for specials
      gint directive_pixels = 0;        // the largest amount of extra space asked for by any directive
      gint directive_space_before = 0; // space requested before note directives e.g. for accidentals
      GList *g = chordval.directives;
      for (; g; g = g->next)
        directive_pixels = MAX (directive_pixels, ((DenemoDirective *) g->data)->minpixels);
      if (chordval.notes)
        {
          theobj->minpixelsalloted = headwidths[headtype];
          //search through notes and their attached directives, find max display space requested
          //use this below

          g = chordval.notes;
          for (; g; g = g->next)
            {
              GList *h = ((note *) g->data)->directives;
              for (; h; h = h->next)
                if (((DenemoDirective *) h->data)->minpixels < 0)
                  directive_space_before -= ((DenemoDirective *) h->data)->minpixels;
                else
                  directive_pixels = MAX (directive_pixels, ((DenemoDirective *) h->data)->minpixels);
            }
        }
      else                      /* a rest */
        theobj->minpixelsalloted = restwidths[resttype];

      // Allow extra space specified by attached LilyPond directives
      theobj->minpixelsalloted += directive_pixels;

      /* 12 pixels for the first dot, 6 for each dot thereafter */
      if (chordval.numdots)
        theobj->minpixelsalloted += 6;
      for (i = 0; i < chordval.numdots; i++)
        theobj->minpixelsalloted += 6;

      theobj->space_before = 0;
      
      if (chordval.hasanacc)
        for (tnode = chordval.notes; tnode; tnode = tnode->next)
          {
            thetone = (note *) tnode->data;
            if (thetone->showaccidental)
              theobj->space_before = MAX (theobj->space_before, thetone->position_of_accidental);
          }

      theobj->space_before += directive_space_before;     
          
      if (chordval.is_reversealigned)
        {
          if (chordval.is_stemup)
            theobj->minpixelsalloted += headwidths[headtype];
          else if (!chordval.hasanacc)
            /* Accidental positioning already accounts for the extra leading
               space that we need for reverse-aligned noteheads, provided
               the chord has an accidental in it somewhere. We only have to
               remark upon noteheads to the left of the stem if there weren't
               any accidentals to position.  */
            theobj->space_before += headwidths[headtype];
        }
      theobj->minpixelsalloted += EXTRAWIDTH;
      break;
    case TUPOPEN:
    case TUPCLOSE:
      /* The real way do this will be with a gdk_string_width. Until
       * then, though: */
      theobj->minpixelsalloted =
#if 0
        40;
#else
        16;
#endif
      theobj->space_before = theobj->minpixelsalloted / 2;
      break;
    case LILYDIRECTIVE:
      {
        DenemoDirective *directive = (DenemoDirective *) theobj->object;
        theobj->minpixelsalloted = directive->minpixels ? directive->minpixels : 16;
        theobj->space_before = theobj->minpixelsalloted / 2;
      }
      break;
    case CLEF:
      theobj->minpixelsalloted = 35;
      theobj->space_before = 0;
      break;
    case KEYSIG:
      theobj->minpixelsalloted = 20;    //needed so find_xes_in_measures assigns space to it without waiting for drawing to do so.
      theobj->space_before = 0;
      break;
    case TIMESIG:
      theobj->minpixelsalloted = 40;
      theobj->space_before = 0;
      break;
    case STEMDIRECTIVE:
      /* The real way do this will be with a gdk_string_width. Until
       * then, though: */
      theobj->minpixelsalloted = 40;
      theobj->space_before = 0;
      break;
    case DYNAMIC:
      theobj->minpixelsalloted = 40;
      theobj->space_before = 0;
      break;
    case GRACE_START:
    case GRACE_END:
      theobj->minpixelsalloted = 16;
      theobj->space_before = theobj->minpixelsalloted / 2;
      break;
    default:
      theobj->minpixelsalloted = 0;
      theobj->space_before = 0;
      break;
    }
}

/**
 *
 * @param mid_c_offset the mid_c_offset of the the tone
 * @param dclef the clef of the current tone
 *
 * @return the height of a tone based on its mid_c_offset and the clef that it's in
 */
gint
calculateheight (gint mid_c_offset, gint dclef)
{
  switch (dclef)
    {
    case DENEMO_TREBLE_CLEF:
      return 5 * LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
      break;                    /* Probably gratuitous */
    case DENEMO_ALTO_CLEF:
      return 2 * LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
      break;
    case DENEMO_G_8_CLEF:
      return LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 1);
      break;
    case DENEMO_BASS_CLEF:
      return -LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
      break;
    case DENEMO_F_8_CLEF:
      return -5 * LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 1);
      break;
    case DENEMO_TENOR_CLEF:
      return LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
      break;
    case DENEMO_SOPRANO_CLEF:
      return LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 6);
      break;
    case DENEMO_FRENCH_CLEF:
      return 6 * LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset);
      break;
    case DENEMO_BARITONE_CLEF:
      return  - HALF_LINE_SPACE * (mid_c_offset);
      break;
    }

  return (0);
}

/**
 * Converts the given offset to a number
 *
 * @param n the offset to convert
 * @return the result of the offset conversion
 */
gint
offsettonumber (gint n)
{
  if (n >= 0)
    return n % 7;
  else
    return (7 - (-n % 7)) % 7;
  /* Not all C implementations conform to the more recent standard on how %
     should operate on negative operands.  */
}

/**
 * converts the int mid_c_offset to the lilypond name
 * returns a gchar * so it will have to be freed
 * 0 returns "c", 1 returns "cis"
 * The octave ",,, or '''" is also appended"
 */

gchar *
mid_c_offsettolily (int mid_c_offset, int enshift)
{
  gint octave, k;
  GString *lilynote = g_string_new ("");

  g_string_append_printf (lilynote, "%c", mid_c_offsettoname (mid_c_offset));
  if (enshift < 0)
    for (k = enshift; k; k++)
      g_string_append_printf (lilynote, "es");
  else
    for (k = enshift; k; k--)
      g_string_append_printf (lilynote, "is");
  octave = mid_c_offsettooctave (mid_c_offset);
  if (octave < 0)
    for (; octave; octave++)
      g_string_append_printf (lilynote, ",");
  else
    for (; octave; octave--)
      g_string_append_printf (lilynote, "\'");

  return g_string_free (lilynote, FALSE);
}

/**
 * converts the mid_c_offset to the correct letter name
 * @param mid_c_offset the mid_c_offset to convert
 * @return the character name of the mid_c_offset
 */
gchar
mid_c_offsettoname (gint mid_c_offset)
{
  gint otn = offsettonumber (mid_c_offset);

  return ((otn + 2) % 7) + 'a';
}

void
note2lilynotename (struct note *noteobject, GString * ret)
{
  gint mid_c_offset = noteobject->mid_c_offset;

  g_string_append_printf (ret, "%c", mid_c_offsettoname (mid_c_offset));
}

void
note2lilyaccidental (struct note *noteobject, GString * ret)
{
  gint enshift = noteobject->enshift;
  gint k;
  if (enshift < 0)
    for (k = enshift; k; k++)
      g_string_append_printf (ret, "es");
  else
    for (k = enshift; k; k--)
      g_string_append_printf (ret, "is");
}

void
note2lilyoctave (struct note *noteobject, GString * ret)
{
  gint mid_c_offset = noteobject->mid_c_offset;
  gint octave = mid_c_offsettooctave (mid_c_offset);
  if (octave < 0)
    for (; octave; octave++)
      g_string_append_printf (ret, ",");
  else
    for (; octave; octave--)
      g_string_append_printf (ret, "\'");
}

void
chord2lilyduration (struct chord *chordobject, GString * ret)
{
  chord2lilybaseduration (chordobject, ret);
  chord2lilynumdots (chordobject, ret);
}

void
chord2lilybaseduration (struct chord *chordobject, GString * ret)
{
  int baseduration = chordobject->baseduration;
  g_string_append_printf (ret, "%d", baseduration);
}

void
chord2lilynumdots (struct chord *chordobject, GString * ret)
{
  int numdots = chordobject->numdots;
  g_string_append_printf (ret, "%d", numdots);
}

/**
 * Calculate a pitches octave from the mid_c_offset
 * @param mid_c_offset the mid_c_offset to use
 * @return the octave of the given mid_c_offset
 */
gint
mid_c_offsettooctave (gint mid_c_offset)
{
  if (mid_c_offset < 0)
    return -((-mid_c_offset + 6) / 7) + 1;
  else
    return (mid_c_offset / 7) + 1;
}

/**
 * g_list_foreach helper function to free the given data
 * @param data the list elements data
 * @param user_data any user supplied data (not used in this case)
 */
void
freeit (gpointer data, gpointer user_data)
{
  g_free (data);
}







/************* routines for calling from debug code ***************/
#include "command/staff.h"

G_GNUC_UNUSED void
printobj (objnode * obj)
{
  DenemoObject *curObj;

  curObj = (DenemoObject *) (obj->data);
  switch (curObj->type)
    {
    case CHORD:
      fprintf (stderr, "\t\t%s type\n", "CHORD");
      break;
    case TUPOPEN:
      fprintf (stderr, "\t\t%s type\n", "TUPOPEN");
      break;
    case TUPCLOSE:
      fprintf (stderr, "\t\t%s type\n", "TUPCLOSE");
      break;
    case CLEF:
      fprintf (stderr, "\t\t%s type\n", "CLEF");
      break;
    case TIMESIG:
      fprintf (stderr, "\t\t%s type\n", "TIMESIG");
      break;
    case KEYSIG:
      fprintf (stderr, "\t\t%s type\n", "KEYSIG");
      break;
    case BARLINE:
      fprintf (stderr, "\t\t%s type\n", "BARLINE");
      break;
    case STEMDIRECTIVE:
      fprintf (stderr, "\t\t%s type\n", "STEMDIRECTIVE");
      break;
    case MEASUREBREAK:
      fprintf (stderr, "\t\t%s type\n", "MEASUREBREAK");
      break;
    case DYNAMIC:
      fprintf (stderr, "\t\t%s type\n", "DYNAMIC");
      break;
    case GRACE_START:
      fprintf (stderr, "\t\t%s type\n", "GRACE_START");
      break;
    case GRACE_END:
      fprintf (stderr, "\t\t%s type\n", "GRACE_END");
      break;
    case LYRIC:
      fprintf (stderr, "\t\t%s type\n", "LYRIC");
      break;
    case FIGURE:
      fprintf (stderr, "\t\t%s type\n", "FIGURE");
      break;
    default:                   /* needs to be up to date with enum in include/denemo/denemo.h */
      fprintf (stderr, "!!!!!unknown object type %x - see enum in denemo.h\n", curObj->type);
      break;
    }
}

G_GNUC_UNUSED void
printobjs (objnode * obj)
{
  objnode *curobj;
  if (obj == NULL)
    {
      fprintf (stderr, "NULL object\n");
      return;
    }
  printobj (obj);
  fprintf (stderr, "previous objects\n");
  curobj = obj;
  while (curobj->prev)
    {
      printobj (curobj->prev);
      curobj = curobj->prev;
    }
  fprintf (stderr, "next objects\n");
  curobj = obj;
  while (curobj->next)
    {
      printobj (curobj->next);
      curobj = curobj->next;
    }
}

G_GNUC_UNUSED void
printmeasure (measurenode * mnode)
{
  if (mnode == NULL)
    {
      fprintf (stderr, "Empty measure\n");
      return;
    }
  printobjs ((objnode *) measure_first_obj_node (mnode));
}

G_GNUC_UNUSED void
printmeasures (staffnode * thestaff)
{
  GList *measure = staff_first_measure_node (thestaff);
  gint measurenum = 1;
  for (measure = staff_first_measure_node (thestaff); measure; measure = measure->next)
    {
      fprintf (stderr, "*************Measure %d *************\n", measurenum++);
      printmeasure (measure);
    }
}

G_GNUC_UNUSED void
printscoreinfo (DenemoMovement * si)
{
  if (si->thescore == NULL)
    {
      fprintf (stderr, "Staff with NULL thescore field\n");
      return;
    }
  printmeasures (si->thescore);
}

/**
 * Function that initializes the code needed for the directory relocation.
 * @return none
 */
void
initdir ()
{
#ifndef G_OS_WIN32
  GError *error = NULL;
  if (!gbr_init (&error) && (error != (GError *) GBR_INIT_ERROR_DISABLED))
    {
      g_debug ("BinReloc failed to initialize:\n");
      g_debug ("Domain: %d (%s)\n", (int) error->domain, g_quark_to_string (error->domain));
      g_debug ("Code: %d\n", error->code);
      g_debug ("Message: %s\n", error->message);
      g_error_free (error);
      g_debug ("----------------\n");
    }
#endif /* not G_OS_WIN32 */
}

extern gchar *gbr_find_pkg_data_dir (const gchar * default_pkg_data_dir, const gchar * pkg_name);

static gchar *DENEMO_datadir = NULL;
const gchar *
get_system_data_dir ()
{
  //DENEMO_datadir?g_debug("datadir is %s at %p", DENEMO_datadir, DENEMO_datadir):g_debug("datadir not yet set");

  
  if (DENEMO_datadir == NULL)
    {
    gchar *env_data = (gchar *)g_getenv ("DENEMO_DATA_DIR");
    if (env_data && *env_data)
      {
        DENEMO_datadir = env_data;
      }      
    else
      {
#ifdef G_OS_WIN32
      gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
      DENEMO_datadir = g_build_filename (rootdir, "share", "denemo", NULL);
      g_message ("rootdir=%s", rootdir);
      g_message ("datadir=%s", DENEMO_datadir);
      g_free (rootdir);
#else /* not G_OS_WIN32 */

#ifdef _MACH_O_

      {
        char path[1024];
        guint size = sizeof (path);
        _NSGetExecutablePath (path, &size);
        gchar *bindir = (gchar *) g_malloc (size);
        if (_NSGetExecutablePath (bindir, &size) == 0)
          g_message ("Using bin path %s", bindir);
        else
          g_critical ("Cannot get bin dir");
        DENEMO_datadir = g_build_filename (g_path_get_dirname (bindir), "..", "share", "denemo", NULL);
        g_message ("OSX set data dir to %s", DENEMO_datadir);
      }
#else
#ifndef ENABLE_BINRELOC
      DENEMO_datadir = g_strdup (PKGDATADIR);
#else
      DENEMO_datadir = gbr_find_pkg_data_dir (PKGDATADIR, PKGNAME);
#endif //ENABLE_BINRELOC

#endif //_MACH_O_
#endif /* not G_OS_WIN32 */
      }
    g_print ("Denemo data expected in %s\n", DENEMO_datadir);
    }
  return DENEMO_datadir;
}

const gchar *
get_prefix_dir (void)
{
  gchar *prefix;
#ifdef G_OS_WIN32
  prefix = g_win32_get_package_installation_directory (NULL, NULL);
#else /* not G_OS_WIN32 */
#ifdef _MACH_O_
  {
    char path[1024];
    guint size = sizeof (path);
    _NSGetExecutablePath (path, &size);
    gchar *bindir = (gchar *) g_malloc (size);
    if (_NSGetExecutablePath (bindir, &size) == 0)
      {
        prefix = g_build_filename (bindir, "..", "..", NULL);
        g_message ("OSX set data prefix to %s", prefix);
      }
    else
      g_critical ("Cannot get bin dir");
  }
#else

#ifndef ENABLE_BINRELOC
  prefix = g_strdup (PREFIX);
#else
  prefix = gbr_find_prefix (PREFIX);
#endif //ENABLE_BINRELOC

#endif //_MACH_O_
#endif //G_OS_WIN32
  return prefix;
}

static gchar *DENEMO_bindir = NULL;
const gchar *
get_system_bin_dir (void)
{

  if (DENEMO_bindir == NULL)
    {
#ifdef G_OS_WIN32
      gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
      DENEMO_bindir = g_build_filename (rootdir, "bin", NULL);
      g_message ("rootdir=%s", rootdir);
      g_message ("bindir=%s", DENEMO_bindir);
      g_free (rootdir);
#else /* not G_OS_WIN32 */

#ifdef _MACH_O_

      {
        char path[1024];
        guint size = sizeof (path);
        _NSGetExecutablePath (path, &size);
        gchar *bin = (gchar *) g_malloc (size);
        if (_NSGetExecutablePath (bin, &size) == 0)
          {
            DENEMO_bindir = g_build_filename (bin, "..", NULL);
            g_message ("Using bin path %s", DENEMO_bindir);
          }
        else
          g_critical ("Cannot get bin dir");

        g_message ("OSX set bin dir to %s", DENEMO_bindir);
      }
#else

#ifndef ENABLE_BINRELOC
      DENEMO_bindir = g_strdup (BINDIR);
#else
      DENEMO_bindir = gbr_find_bin_dir (BINDIR);
#endif //ENABLE_BINRELOC

#endif //_MACH_O_
#endif /* not G_OS_WIN32 */
    }
  return DENEMO_bindir;
}

/** UNUSED
const gchar *
get_system_conf_dir ()
{
  static gchar *confdir = NULL;
  if (confdir == NULL)
    {
#ifdef G_OS_WIN32
      gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
      confdir = g_build_filename (rootdir, "etc", "denemo", NULL);
      g_free (rootdir);
#else // not G_OS_WIN32
#ifdef _MACH_O_

      {
        char path[1024];
        guint size = sizeof (path);
        _NSGetExecutablePath (path, &size);
        gchar *bindir = (gchar *) g_malloc (size);
        if (_NSGetExecutablePath (bindir, &size) == 0)
          g_debug ("using bin path %s\n", bindir);
        else
          g_critical ("Cannot get bin dir\n");
        confdir = g_build_filename (g_path_get_dirname (bindir), "..", "etc", "denemo", NULL);
        g_debug ("OSX set conf dir to %s\n", confdir);
      }
#else

#ifndef ENABLE_BINRELOC
      confdir = g_build_filename (SYSCONFDIR, NULL);
#else
      confdir = g_build_filename (gbr_find_etc_dir (SYSCONFDIR), "denemo", NULL);
#endif //ENABLE_BINRELOC

#endif //_MACH_O_
#endif // not G_OS_WIN32
    }
  return confdir;
}
*/

const gchar *
get_system_locale_dir ()
{
  static gchar *localedir = NULL;
  if (localedir == NULL)
    {
#ifdef G_OS_WIN32
      gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
      localedir = g_build_filename (rootdir, "share", "locale", NULL);
      g_free (rootdir);
#else /* not G_OS_WIN32 */
#ifdef _MACH_O_

      {
        char path[1024];
        guint size = sizeof (path);
        _NSGetExecutablePath (path, &size);
        gchar *bindir = (gchar *) g_malloc (size);
        if (_NSGetExecutablePath (bindir, &size) == 0)
          g_message ("Using bin path %s", bindir);
        else
          g_critical ("Cannot get bin dir");
        localedir = g_build_filename (g_path_get_dirname (bindir), "..", "share", "locale", NULL);
        g_message ("OSX set locale dir to %s", localedir);
      }
#else
#ifndef ENABLE_BINRELOC
      /* it seems to be the standard way (no binreloc)
       * to set the path of translations this way:
       * messages are in $LOCALEDIR/$LANG/denemo
       */
      localedir = g_strdup (LOCALEDIR);
#else /* ENABLE_BINRELOC */
      /* binreloc says it is disabled even with built thanks to
       * --enable-binreloc... So, searhing falls back to
       *  $LOCALEDIR/denemo/$LANG which is not a valid path
       */
      localedir = gbr_find_locale_dir (LOCALEDIR);
#endif /* ENABLE_BINRELOC */
#endif
#endif /* not G_OS_WIN32 */
    }
  return localedir;
}

const gchar *
get_system_font_dir ()
{
  static gchar *fontdir = NULL;
  if (fontdir == NULL)
    {
#ifdef G_OS_WIN32
      gchar *prefix = g_win32_get_package_installation_directory (NULL, NULL);
#else
      gchar *prefix = g_build_filename (get_prefix_dir (), NULL);
#endif
      fontdir = g_build_filename (prefix, "share", "fonts", "truetype", "denemo", NULL);
    }
  return fontdir;
}

void
kill_process (GPid pid)
{
#ifdef G_OS_WIN32
  TerminateProcess (pid, 0);
#else /* not G_OS_WIN32 */
  kill (pid, SIGTERM);
#endif /* not G_OS_WIN32 */
  g_spawn_close_pid (pid);
}


void
init_denemo_notenames (void)
{

  define_scheme_literal_variable ("Denemo-Note0", NOTE0, NULL);
  define_scheme_literal_variable ("Denemo-Rest0", REST0, NULL);
  define_scheme_literal_variable ("Denemo-Note1", NOTE1, NULL);
  define_scheme_literal_variable ("Denemo-Rest1", REST1, NULL);
  define_scheme_literal_variable ("Denemo-Note2", NOTE2, NULL);
  define_scheme_literal_variable ("Denemo-Rest2", REST2, NULL);
  define_scheme_literal_variable ("Denemo-Note3", NOTE3, NULL);
  define_scheme_literal_variable ("Denemo-Rest3", REST3, NULL);
  define_scheme_literal_variable ("Denemo-Note4", NOTE4, NULL);
  define_scheme_literal_variable ("Denemo-Rest4", REST4, NULL);
  define_scheme_literal_variable ("Denemo-Note5", NOTE5, NULL);
  define_scheme_literal_variable ("Denemo-Rest5", REST5, NULL);
  define_scheme_literal_variable ("Denemo-Note6", NOTE6, NULL);
  define_scheme_literal_variable ("Denemo-Rest6", REST6, NULL);
  define_scheme_literal_variable ("Denemo-Note7", NOTE7, NULL);
  define_scheme_literal_variable ("Denemo-Rest7", REST7, NULL);
  define_scheme_literal_variable ("Denemo-Note8", NOTE8, NULL);
  define_scheme_literal_variable ("Denemo-Rest8", REST8, NULL);

}

#define HIGHLIGHT "<span background=\"lightblue\"> "

/* markup the passed string to be in the denemo music font
* caller must free the returned string
*/
gchar *
music_font (gchar * str)
{
  GString *s = g_string_new ("");
  gint c = *str;
  for (c = *str; c; c = *++str)
    switch (c)
      {
      case '0':
        g_string_append (s, " " NOTE0 " ");
        break;
      case HIGHLIGHT_OFFSET + '0':
        g_string_append (s, HIGHLIGHT NOTE0 " </span>");
        break;

      case '1':
        g_string_append (s, " " NOTE1 " ");
        break;
      case HIGHLIGHT_OFFSET + '1':
        g_string_append (s, HIGHLIGHT NOTE1 " </span>");
        break;
      case '2':
        g_string_append (s, " " NOTE2 " ");
        break;
      case HIGHLIGHT_OFFSET + '2':
        g_string_append (s, HIGHLIGHT NOTE2 " </span>");
        break;
      case '3':
        g_string_append (s, " " NOTE3 " ");
        break;
      case HIGHLIGHT_OFFSET + '3':
        g_string_append (s, HIGHLIGHT NOTE3 " </span>");
        break;
      case '4':
        g_string_append (s, " " NOTE4 " ");
        break;
      case HIGHLIGHT_OFFSET + '4':
        g_string_append (s, HIGHLIGHT NOTE4 " </span>");
        break;
      case '5':
        g_string_append (s, " " NOTE5 " ");
        break;
      case HIGHLIGHT_OFFSET + '5':
        g_string_append (s, HIGHLIGHT NOTE5 " </span>");
        break;
      case '6':
        g_string_append (s, " " NOTE6 " ");
        break;
      case HIGHLIGHT_OFFSET + '6':
        g_string_append (s, HIGHLIGHT NOTE6 " </span>");
        break;
      case '7':
        g_string_append (s, " " NOTE7 " ");
        break;
      case HIGHLIGHT_OFFSET + '7':
        g_string_append (s, HIGHLIGHT NOTE7 " </span>");
        break;
      case '8':
        g_string_append (s, " " NOTE8 " ");
        break;
      case HIGHLIGHT_OFFSET + '8':
        g_string_append (s, HIGHLIGHT NOTE8 " </span>");
        break;

      case 'r':
        g_string_append (s, " " REST0 " ");
        break;
      case HIGHLIGHT_OFFSET + 'r':
        g_string_append (s, HIGHLIGHT REST0 " </span>");
        break;

      case 's':
        g_string_append (s, " " REST1 " ");
        break;
      case HIGHLIGHT_OFFSET + 's':
        g_string_append (s, HIGHLIGHT REST1 " </span>");
        break;
      case 't':
        g_string_append (s, " " REST2 " ");
        break;
      case HIGHLIGHT_OFFSET + 't':
        g_string_append (s, HIGHLIGHT REST2 " </span>");
        break;
      case 'u':
        g_string_append (s, " " REST3 " ");
        break;
      case HIGHLIGHT_OFFSET + 'u':
        g_string_append (s, HIGHLIGHT REST3 " </span>");
        break;
      case 'v':
        g_string_append (s, " " REST4 " ");
        break;
      case HIGHLIGHT_OFFSET + 'v':
        g_string_append (s, HIGHLIGHT REST4 " </span>");
        break;
      case 'w':
        g_string_append (s, " " REST5 " ");
        break;
      case HIGHLIGHT_OFFSET + 'w':
        g_string_append (s, HIGHLIGHT REST5 " </span>");
        break;
      case 'x':
        g_string_append (s, " " REST6 " ");
        break;
      case HIGHLIGHT_OFFSET + 'x':
        g_string_append (s, HIGHLIGHT REST6 " </span>");
        break;
      case 'y':
        g_string_append (s, " " REST7 " ");
        break;
      case HIGHLIGHT_OFFSET + 'y':
        g_string_append (s, HIGHLIGHT REST7 " </span>");
        break;
      case 'z':
        g_string_append (s, " " REST8 " ");
        break;
      case HIGHLIGHT_OFFSET + 'z':
        g_string_append (s, HIGHLIGHT REST8 " </span>");
        break;
      default:
        g_string_append_c (s, c);
      }
  return g_string_free (s, FALSE);

}

void
set_title_bar (DenemoProject * gui)
{
  if (Denemo.non_interactive)
    return;
  gchar *title;
  if (gui->tabname && gui->tabname->len)
    title = gui->tabname->str;
  else
    title = _("(Untitled)");
  title = g_strdup_printf ("%s%c", title, gui->notsaved ? '*' : ' ');
  gtk_window_set_title (GTK_WINDOW (Denemo.window), title);
  gchar *base = g_path_get_basename (title);
  gint index = g_list_index (Denemo.projects, gui);
  if (index < 0)
    {
      g_critical ("project is %p is not in list of projects, first tab is  %p\n", gui, Denemo.projects->data);
      return;

    }
  GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (Denemo.notebook), index);
  if (page == NULL)
    {
      g_critical ("Bad page, passed project is %p, first tab is  %p\n", gui, Denemo.projects->data);
      return;

    }
  gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (Denemo.notebook), page, base);

  gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (Denemo.notebook), page, base);


  g_free (title);
  g_free (base);
}

static const gchar *
enshift_string (gint enshift)
{
  switch (enshift)
    {
    case -2:
      return "𝄫";
    case -1:
      return "♭";
    case 0:
      return " ";
    case 1:
      return "♯";
    case 2:
      return "𝄪";
    default:
      return _("Error");
    }
}

gint64 thetime;

static void
start_editing_timer (void)
{

  thetime = g_get_monotonic_time ();

}

static void
stop_editing_timer (void)
{
  gint64 thistime = g_get_monotonic_time ();
  if ((thetime > 0) && (thetime < thistime))
    Denemo.project->total_edit_time += (thistime - thetime) / 1000000;
}

void
reset_editing_timer (void)
{
  thetime = 0;
}

gchar *
time_spent_editing ()
{
  gint seconds = Denemo.project->total_edit_time;
  gint days = seconds / (24 * 60 * 60);
  gint hours;
  gint minutes;

  seconds -= days * (24 * 60 * 60);
  hours = (seconds / (60 * 60));
  seconds -= hours * (60 * 60);
  minutes = seconds / 60;
  seconds -= minutes * 60;
  gchar *format = days?"%d days %d hours %d minutes %d seconds"
                        :hours?"%.0d%d hours %d minutes %d seconds"
                        :minutes?"%.0d%.0d%d minutes %d seconds"
                        :"%.0d%.0d%.0d%d seconds: ";
  return g_strdup_printf (format, days, hours, minutes, seconds);
}

/* set the status of the current musical score - its change count and
   title bar and status bars.
   DenemoProject *gui the musical score.
   gboolean change TRUE = a change has just been made
                   FALSE = the score is to be assumed saved
*/
void
score_status (DenemoProject * gui, gboolean change)
{
  if (change)
    {
      gboolean just_changed = !gui->notsaved;
      gui->notsaved = TRUE;
      gui->changecount++;
      gui->movement->changecount++;
      if (just_changed)
        if (!Denemo.non_interactive)
          start_editing_timer ();

    }
  else
    {
      gui->notsaved = FALSE;
      if (!Denemo.non_interactive)
        stop_editing_timer ();
    }
  if (!Denemo.non_interactive)
    {
      set_title_bar (gui);
      write_status (gui);
    }
}

/**
 * If the curObj is a chord with a note(s)
 * return the first note at or below cursory, or the last note
 * else return NULL
 */
note *
findnote (DenemoObject * curObj, gint cursory)
{
  note *curnote = NULL;
  if (curObj && curObj->type == CHORD && ((chord *) curObj->object)->notes)
    {
      GList *notes = ((chord *) curObj->object)->notes;
      for (; notes; notes = notes->next)
        {
          curnote = (note *) notes->data;
          //g_debug("comparing %d and %d\n", cursory, curnote->y);
          if (cursory <= curnote->mid_c_offset)
            break;
        }

    }
  return curnote;
}

/**
 * If the curObj is a chord with a note(s)
 * return the note at cursory else return NULL
 */
note *
findnote_strict (DenemoObject * curObj, gint cursory)
{
  note *curnote = NULL;
  if (curObj && curObj->type == CHORD && ((chord *) curObj->object)->notes)
    {
      GList *notes = ((chord *) curObj->object)->notes;
      for (; notes; notes = notes->next)
        {
          curnote = (note *) notes->data;
          //g_debug("comparing %d and %d\n", cursory, curnote->y);
          if (cursory == curnote->mid_c_offset)
            return curnote;
        }

    }
  return NULL;
}

/* get a fret diagram for the chord at the cursor or before the cursor if not on the chord  */
gchar *
get_fretdiagram_as_markup (void)
{
  DenemoProject *gui = Denemo.project;
  DenemoObject *curObj;
  if (!Denemo.project || !(Denemo.project->movement) || !(Denemo.project->movement->currentobject) || !(curObj = Denemo.project->movement->currentobject->data) || !(DENEMO_OBJECT_TYPE_NAME (curObj)))
    return NULL;
  if (curObj->type != CHORD && Denemo.project->movement->currentobject->next)
    curObj = Denemo.project->movement->currentobject->next->data;
  if (gui->lilysync != gui->changecount)
    refresh_lily_cb (NULL, Denemo.project);
  if (curObj->lilypond)
    {
      gchar *text = g_strdup_printf ("\\score{\n\\DenemoGlobalTranspose\n\\new FretBoards {%s}\n\\layout{indent=0.0}\n}", curObj->lilypond);
      return text;
    }
  return NULL;
}
static gint note1, note2, acc1, acc2, oct1, oct2;

static void set_encoding (gint val, gboolean one)
{
      if(val&7)
        {
            val = (val | (0xFFFF - 7)) & val;
            if (one)
                note1 = val;
            else 
                note2 = val;
            return;
        }
    val = val>>4;
    if(val&7)
        {
            val = (val | (0xFFFF - 7)) & val;
            if (one)
                acc1 = val;
            else 
                acc2 = val;
            return;
        }
    val = val>>4; // a total shift of >>8 now
    if(val&7)
        {
            if (one)
                oct1 = val;
            else 
                oct2 = val;
            return;
        }
}
static void set_note_name_encoding1 (GtkWidget*w, gint val)
{
        set_encoding (val,TRUE);
}
static void set_note_name_encoding2 (GtkWidget*w, gint val)
{
        set_encoding (val, FALSE);
}
static gchar* note_name[]= {"A", "B","C","D","E","F","G" };
static gchar* note_name_lc[]= {"a", "b","c","d","e","f","g" };
static gchar* acc_name[]= {"♮", "♯","♭","𝄪","𝄫", NULL,NULL, ""};
static gchar* acc_name_lily[]= {"", "is","es","isis","eses",NULL,NULL, ""};

static gchar *oct_name[] = {"1", "2","3","4","5","6","7","8"};
static gchar *oct_name_lily[] = { ",,,,", ",,,",",,", ",", "", "'", "''", "''"};

static gchar *decode_values (gint nm, gint acc, gint oct)
{
    gchar *name = g_strdup (note_name_lc[nm - 1]);
    gchar *accname = acc_name_lily [acc];
    gchar *octname = oct_name_lily [oct]; //g_print ("decoded %d %d %d %s %s %s\n",nm, acc, oct, name, accname, octname);
    return g_strconcat (name, accname, octname, NULL);
}

static gint decode_note (gchar *note)
{
  gint ret = 0;
  if (*note)
    {
      gchar *c;
      ret =  1 + tolower (*note)-'a';
      if (ret<1 || ret>7)
        ret = 0;
       c = note+1; 
      if(g_str_has_prefix (c, "eses"))
           {c +=4;   ret += (4<<4);}
      else if (g_str_has_prefix (c, "es"))
         {c +=2;   ret += (2<<4);}
      else if (g_str_has_prefix (c, "isis"))
         {c +=5;   ret += (3<<4);}        
     else if (g_str_has_prefix (c, "is"))
        {c +=2;   ret += (1<<4);}
     gint oct=0;   
     while (*c==',')
            {
               oct++;
               c++; 
            }
      if (oct)
            {
              ret += (4-oct)<<(4+4);
            }
    if (oct==0)   // no ,,,     
        {
            while (*c=='\'')
                {
                   oct++;
                   c++; 
                }
          if (oct)
                {
                  ret += (4+oct)<<(4+4);
                }            
          else ret += (4+oct)<<(4+4);//can include in above case  
      }    
                  
    }
  // g_print ("Encoded %s as 0x%x", note, ret);
   return ret; 
}
gchar *
notes_choice_dialog (gint number_of_notes /* 1 or 2 */, gchar *initial_value, gchar *meaning)
{
    GtkWidget *dialog;
    gchar *text = NULL;
    gint j;
    gchar *title = (number_of_notes==1) ? _("Select Note") : _("Select Notes");
    gint enc1=0, enc2=0;
    if (initial_value)
        {
          gchar first[50], second[50];
          if ((number_of_notes == 2) && ( 2 == sscanf (initial_value, "%50s%50s", first, second)))
            {
                
                enc1 = decode_note (first);
                enc2 = decode_note (second);
                
            }
          else
                if ( 1 == sscanf (initial_value, "%50s", first))
                    {
                        enc1 = decode_note (first);
                    }
         note1 = enc1 & 7;   
         note2 = enc2 & 7;
          
         enc1 >>=4;
         enc2 >>= 4;
         acc1 = enc1 & 7;   
         acc2 = enc2 & 7;
         
         enc1 >>=4;
         enc2 >>= 4;
         oct1 = enc1 & 7;   
         oct2 = enc2 & 7;  
         //g_print ("Initial values are %s %s %s", note_name [note1], acc_name[acc1], oct_name[oct1]);        
        }
    
    
    dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (Denemo.window), 
        (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
         _("Done"), GTK_RESPONSE_ACCEPT, 
         _("Cancel"), GTK_RESPONSE_CANCEL,
         NULL);
    GtkWidget *frame;
    GtkWidget *area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
    GtkWidget *hbox = gtk_hbox_new (FALSE, 1);
    gtk_container_add (GTK_CONTAINER (area), hbox);
    frame =  gtk_frame_new (_("Note Name"));   
    gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);     
    GtkWidget *vbox = gtk_vbox_new (FALSE, 1);
    gtk_container_add (GTK_CONTAINER (frame), vbox);
    for (j=0;j<number_of_notes;j++)
        {
            if (j)
                { 
                    GtkWidget *label = gtk_label_new (meaning?meaning:"----------->");
                    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
                    frame =  gtk_frame_new (_("Note Name"));   
                    gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);     
                    vbox = gtk_vbox_new (FALSE, 1);  
                    gtk_container_add (GTK_CONTAINER (frame), vbox); 
                }
            GtkWidget *button;
            void (*set_note_name_encoding)(GtkWidget*, gint)  = j? set_note_name_encoding2: set_note_name_encoding1;
//buttons for note name   
                
            GtkWidget *button0 = gtk_radio_button_new_with_label_from_widget (NULL,note_name[0]);
            gtk_box_pack_start (GTK_BOX (vbox), button0, TRUE, TRUE, 0);

            if (note1==0) //no initial values set
                {
                    note1 = note2 = 1; //initial choice "A"
                    acc1 = acc2 = 7; //initial choice natural
                    oct1 = oct2 = 4; //initial choice
                }

            gint i;
            for (i=1; i<7; i++)
            {
                GtkWidget *button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(button0), note_name[i]);
                if (i == ((j?note2:note1)-1)) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
//NOTE: setting this active actually causes the clicked signal to be fired! so must do it before connecting the handler

                g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (set_note_name_encoding), GINT_TO_POINTER(i+1));

                gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
            }
            g_signal_connect (G_OBJECT (button0), "clicked", G_CALLBACK (set_note_name_encoding),GINT_TO_POINTER(1));
            
            
            
//buttons for accidental
            
            frame =  gtk_frame_new (_("Accidental"));   
            gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
            GtkWidget *accbox = gtk_vbox_new (FALSE, 1);
            gtk_container_add (GTK_CONTAINER (frame), accbox);
            button0 = gtk_radio_button_new_with_label_from_widget (NULL,acc_name[0]);
            gtk_box_pack_start (GTK_BOX (accbox), button0, TRUE, TRUE, 0);
            for (i=1; i<5; i++)
                {
                    GtkWidget *button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(button0), acc_name[i]);
            
                    if ((i==0 && ((j?acc2:acc1)==7))
                        || (i == ((j?acc2:acc1)))) 
                            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
                    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (set_note_name_encoding), GINT_TO_POINTER(i<<4));
                    gtk_box_pack_start (GTK_BOX (accbox), button, TRUE, TRUE, 0);
                }
            g_signal_connect (G_OBJECT (button0), "clicked", G_CALLBACK (set_note_name_encoding),GINT_TO_POINTER(7<<4));            

                
//buttons for octave
        if(number_of_notes == 2) //only offer octave for picking note pairs
            {
            frame = gtk_frame_new (_("Octave"));   
            gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
            GtkWidget *octbox = gtk_vbox_new (FALSE, 1);
            gtk_container_add (GTK_CONTAINER (frame), octbox);
            button0 = gtk_radio_button_new_with_label_from_widget (NULL,oct_name[7]);
            gtk_box_pack_start (GTK_BOX (octbox), button0, TRUE, TRUE, 0);
            for (i=6; i>-1; i--)
                {
                    GtkWidget *button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(button0), oct_name[i]);
                    if (i == ((j?oct2:oct1) -1)) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
                    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (set_note_name_encoding), GINT_TO_POINTER((i+1)<<8));
                    gtk_box_pack_start (GTK_BOX (octbox), button, TRUE, TRUE, 0);
                } 
           g_signal_connect (G_OBJECT (button0), "clicked", G_CALLBACK (set_note_name_encoding),GINT_TO_POINTER(8<<8));            
          }
                
        }

    gtk_widget_show_all (dialog);
    gtk_window_present(GTK_WINDOW (dialog));
    //gtk_window_set_urgency_hint (GTK_WINDOW (dialog), TRUE); causes flashing on windows even when selected :(
    gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
    gint result = gtk_dialog_run (GTK_DIALOG (dialog));
    if (result == GTK_RESPONSE_ACCEPT)
    {
        gchar *text1 = decode_values (note1, acc1, oct1);
        gchar *text2 = decode_values (note2, acc2, oct2); 
        text = g_strdup_printf ("%s%s%s", text1, number_of_notes==2?" ":"", number_of_notes==2?text2:"");//g_print ("Octave 2 is %d  %s so %s\n", oct2, text2, text);
        g_free (text1);
        g_free (text2);
    }
  gtk_widget_destroy (dialog);
  return text;
}

gchar *get_chord_notes (void) {
  DenemoObject *curObj;
  chord *thechord;
  note *thenote;
  GString *str = g_string_new ("");   
  if (!Denemo.project || !(Denemo.project->movement) || !(Denemo.project->movement->currentobject) || !(curObj = Denemo.project->movement->currentobject->data) || (curObj->type != CHORD) || !(thechord = (chord *) curObj->object) || !(thechord->notes) || !(thenote = (note *) thechord->notes->data))
    return NULL;
  else
    {
      GList *g;
      for (g = thechord->notes; g; g = g->next)
        {
          thenote = (note *) g->data;
          gchar *name = mid_c_offsettolily (thenote->mid_c_offset, thenote->enshift);
          str = g_string_append (str, name);
          if (g->next)
            str = g_string_append (str, " ");
        }
 
    return g_string_free (str, FALSE);  
    }
return NULL;
}

/*
 * Truncate a label widget's text to fit the widget's width. FIXME very rough guess as to width - pango calls needed to do this properly.
 */
void truncate_label (GtkWidget *label, gchar *str)
	{
	GdkRectangle alloc;
	gint width;
	if (label)
		{
			gtk_widget_get_allocation (label, &alloc);
			width = (1.75 * alloc.width)/(Denemo.prefs.fontsize);
			//g_print ("Truncate label: alloc is %d number of chars = %d???\n\n\n", alloc.width, (3*alloc.width)/(2*Denemo.prefs.fontsize));
		}
	else {
		gtk_widget_get_allocation (Denemo.input_label, &alloc);
		width = (1.75 * alloc.width)/(Denemo.prefs.fontsize);
		width -= 30;//allow 30 char for the "Help (Use Fn12 to Repeat)" string which is markup so actually includes more characters
	 }
	gchar *end;
	if ((width > 30) && g_utf8_strlen (str, -1) > width) 
		{gint i;
			for (end = str, i = 1; i < width; i++, end = g_utf8_next_char (end))
				;//set end to i'th character
			end = g_utf8_find_prev_char (str, end);
			end = g_utf8_find_prev_char (str, end);
			*end = '.';*(end+1) = '.';*(end+2) = '.';*(end+3) = 0;
		}
	}


/****************
 * write the status bar

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

void
write_status (DenemoProject * gui)
{
  if (Denemo.non_interactive)
    return;

  gint minutes = 0;
  gdouble seconds = 0.0;
  gdouble early = 0.0, late = 0.0;
  gchar *selection;
  if (gui->movement == NULL)
    return;

  static GList *last_object;
  if ((get_wysiwyg_info ()->stage != SelectingFarEnd))
      if (gui->movement->currentobject)
        {
          if ((last_object != gui->movement->currentobject))
            {
              if (get_wysiwyg_info ()->stage != TypesetForPlaybackView)
                get_wysiwyg_info ()->stage = STAGE_NONE;    //remove the mark in the printview window as the cursor has moved
              get_wysiwyg_info ()->Mark.width = 0;
              gtk_widget_queue_draw (Denemo.printarea);
            }
          last_object = gui->movement->currentobject;
        }

  if (gui->movement->currentobject && gui->movement->currentobject->data)
    {
      DenemoObject *curObj = gui->movement->currentobject->data;
      if ((gui->movement->smfsync == gui->movement->changecount))
        {
          early = curObj->earliest_time, late = curObj->latest_time;
        }

      switch (curObj->type)
        {
        case CHORD:
          {
            chord *thechord = ((chord *) curObj->object);
            selection = g_strdup_printf ("%s%s%s%s%s%s%s%s%s",
                                         thechord->notes ? (g_list_length (thechord->notes) > 1 ? _("Chord ") : _("Note ")) : _("Rest "), thechord->slur_begin_p ? _(", begin slur") : "", thechord->slur_end_p ? _(", end slur") : "", thechord->is_tied ? _(", tied") : "", thechord->crescendo_begin_p ? _(", begin cresc.") : "", thechord->crescendo_end_p ? _(", end cresc.") : "", thechord->diminuendo_begin_p ? _(", begin dim.") : "", thechord->diminuendo_end_p ? _(", end dim.") : "",
                                         thechord->is_grace ? _(", grace note") : "");
            if (thechord->notes)
              {
                GList *g;
                for (g = thechord->notes; g; g = g->next)
                  {
                    note *thenote = (note *) g->data;
                    GList *h;
                    for (h = thenote->directives; h; h = h->next)
                      {
                        DenemoDirective *directive = (DenemoDirective *) h->data;
                        if (directive->postfix || directive->prefix)
                          {
                            gchar *old = selection;
                            selection = g_strdup_printf ("%.50s (%s) %.50s", directive->prefix ? directive->prefix->str : "", selection, directive->postfix ? directive->postfix->str : "");
                            g_free (old);
                          }
                      }
                  }
              }
          }
          break;

        case TUPOPEN:
          selection = g_strdup_printf (_("Tuplet %d/%d"), ((tupopen *) curObj->object)->numerator, ((tupopen *) curObj->object)->denominator);
          break;
        case TUPCLOSE:
          selection = g_strdup_printf (_("End tuplet"));
          break;
        case CLEF:
          selection = g_strdup_printf (_("Clef change"));
          break;
        case TIMESIG:
          selection = g_strdup_printf (_("Time signature change"));
          break;
        case KEYSIG:
          selection = g_strdup_printf (_("Key signature change"));
          break;
        case STEMDIRECTIVE:
          selection = g_strdup_printf (_("Stem directive: %s"), ((stemdirective *) curObj->object)->type == DENEMO_STEMDOWN ? _("stem down") : ((stemdirective *) curObj->object)->type == DENEMO_STEMUP ? _("stem up") : _("normal stemming"));
          break;
        case DYNAMIC:
          selection = g_strdup_printf (_("Dynamic: %s"), ((dynamic *) curObj->object)->type->str);
          break;

        case LILYDIRECTIVE:
          {
            DenemoDirective *directive = (DenemoDirective *) curObj->object;
            selection = g_strdup_printf (_("Directive:(%.20s) %.20s%.20s"), directive->tag ? directive->tag->str : _("Unknown Tag"), directive->layouts ? _("Not all layouts") : "", directive->postfix ? directive->postfix->str : directive->prefix ? directive->prefix->str : directive->graphic_name ? directive->graphic_name->str : directive->display ? directive->display->str : "empty");
          }
          break;
        default:
          selection = g_strdup_printf (_("Cursor on an unknown object"));
        }

      //DenemoMeasure *measure = gui->movement->currentmeasure->data;
      //selection = g_strdup_printf ("%s %s %d/%d %d", selection, curObj->clef?get_clef_name (curObj->clef->type):"NULL", measure->timesig?measure->timesig->time1:0, measure->timesig?measure->timesig->time2:0, curObj->keysig?curObj->keysig->number:0xFFFF);


    }
  else
    selection = g_strdup_printf (_("Cursor not on any object"));

  GString *status = g_string_new (_("Movement"));
  gint index = g_list_index (gui->movements, gui->movement);
  const gchar *dur = "";
  switch (get_prevailing_duration ())
    {
    case 0:
      dur = NOTE0;
      break;
    case 1:
      dur = NOTE1;
      break;
    case 2:
      dur = NOTE2;
      break;
    case 3:
      dur = NOTE3;
      break;
    case 4:
      dur = NOTE4;
      break;
    case 5:
      dur = NOTE5;
      break;
    case 6:
      dur = NOTE6;
      break;
    case 7:
      dur = NOTE7;
      break;
    case 8:
      dur = NOTE8;
      break;


    }
  g_string_printf (status, "%s%s %d: %s: ", enshift_string (gui->movement->pending_enshift), dur, index + 1, selection);

  DenemoMeasure *meas = (DenemoMeasure*)gui->movement->currentmeasure->data;
     

  if (gui->movement->smf && (gui->movement->smfsync == gui->movement->changecount) && Denemo.prefs.playback_controls)
    g_string_append_printf (status, _("start %.2f end %.2f"), early, late);
  else
    g_string_append_printf (status, _(" Staff %d Measure %d Position %d %s"), gui->movement->currentstaffnum, meas->measure_number, gui->movement->cursor_x + 1, (gui->movement->cursor_appending || !gui->movement->currentobject) ? _("Appending") : _("Not Appending") /*not understood this one... , gui->movement->cursoroffend?"Off End":"Not Off End" */ );

  if (Denemo.prefs.midi_in_controls)
    {
      gchar *thesharp = sharpest ();
      gchar *theflat = flattest ();
      g_string_append_printf (status, " |%s - %s|", theflat, thesharp);
      g_free (theflat);
      g_free (thesharp);
    }

  g_free (selection);
    
  if (status->len > 20)
	truncate_label (Denemo.statuslabel, status->str);
  gchar *end_valid;
  if (!g_utf8_validate (status->str, -1, (const gchar **) &end_valid))
    *end_valid = '\0';

  gtk_label_set_text (GTK_LABEL (Denemo.statuslabel), status->str);

  g_string_free (status, TRUE);
  update_object_info ();
}

void
write_input_status (void)
{
  if (Denemo.non_interactive)
    return;
  gtk_label_set_markup (GTK_LABEL (Denemo.input_label), Denemo.input_filters->str);
}

/**
 * Display a message box giving two choices, return FALSE if second is chosen.
 * 
 */
gboolean
confirm_first_choice (gchar *title, gchar * primary, gchar * secondary)
{
  GtkWidget *dialog;
  gboolean r = 0;

  dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", title, NULL);

  gtk_dialog_add_buttons (GTK_DIALOG (dialog), primary,  GTK_RESPONSE_YES, secondary, GTK_RESPONSE_NO, NULL);
  gtk_widget_show_all (dialog);
  r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES);
  gtk_widget_destroy (dialog);
  return r;
}
/**
 * Display a message box asking primary & secondary messages
 * @return TRUE if the OK button was clicked or Enter pressed
 */
gboolean
confirm (gchar * primary, gchar * secondary)
{
  GtkWidget *dialog;
  gboolean r = 0;

  dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", primary);

  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary);
  gtk_widget_show_all (dialog);
  r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES);
  gtk_widget_destroy (dialog);
  return r;
}
gboolean
choose_option (gchar * title, gchar * primary, gchar * secondary)
{
  return choose_option_or_cancel (title, primary, secondary, FALSE) > 0;
}
gint
choose_option_or_cancel (gchar * title, gchar * primary, gchar * secondary, gboolean cancel_button)
{
  GtkWidget *dialog;
  gint r;
  dialog = (cancel_button?
    gtk_dialog_new_with_buttons (title, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), primary, GTK_RESPONSE_ACCEPT, secondary, GTK_RESPONSE_REJECT, _("Cancel"), GTK_RESPONSE_CANCEL, NULL):
    gtk_dialog_new_with_buttons (title, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), primary, GTK_RESPONSE_ACCEPT, secondary, GTK_RESPONSE_REJECT, NULL));
  gtk_window_set_urgency_hint (GTK_WINDOW (dialog), TRUE);
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (Denemo.window));
  gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
  r = gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
  if (r==GTK_RESPONSE_ACCEPT)
    return 1;
  if (r==GTK_RESPONSE_REJECT)
    return 0;
  return -1;
}


/* free a GString and the string it holds, and set the pointer to it to NULL */
void
nullify_gstring (GString ** s)
{
  if (*s)
    g_string_free (*s, TRUE);
  *s = NULL;
}


static gchar *
choose_file_or_directory (gchar * title, gchar * startdir, GList * extensions, gboolean dir)
{
  GtkWidget *dialog;
  gchar *filename = NULL;
  dialog = gtk_file_chooser_dialog_new (title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Open"), GTK_RESPONSE_ACCEPT, NULL);
  if(dir)
    gtk_file_chooser_set_action (GTK_FILE_CHOOSER(dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
  if (extensions)
    {
      GtkFileFilter *filter = gtk_file_filter_new ();
      GString *filter_description = g_string_new ("");
      for (extensions; extensions; extensions = extensions->next)
        {
          gtk_file_filter_add_pattern (filter, (gchar *) extensions->data);
          g_string_append_printf (filter_description, "%s ", (gchar *) extensions->data);
        }
      gtk_file_filter_set_name (filter, filter_description->str);
      gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
    }
  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), startdir);

  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
    {
      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
    }

  gtk_widget_destroy (dialog);
  return filename;
}

/* dialog to get a filename from the user
 */
gchar *
choose_file (gchar * title, gchar * startdir, GList * extensions)
{
 return choose_file_or_directory (title, startdir, extensions, FALSE);
}
/* dialog to get a directory from the user
 */
gchar *
choose_directory (gchar * title, gchar * startdir, GList * extensions)
{
 return choose_file_or_directory (title, startdir, extensions, TRUE);
}

gchar *try_to_find_file (gchar *base, gchar *filename)
{
  gchar *c;
  gchar *name = g_build_filename (base, filename, NULL);
    if (g_file_test (name, G_FILE_TEST_IS_REGULAR))
      return name;
  g_free (name);
  name = g_strdup (filename);
  for (c=name;*c; c++)
    {
      if (*c && (*c==G_DIR_SEPARATOR) || (*c=='\\') && *(c+1))
        {
            gchar *try = g_build_filename (base, c+1, NULL); g_print ("try %s c now %s\n", try, c);
            if (g_file_test (try, G_FILE_TEST_IS_REGULAR))
              {
                g_string_assign (Denemo.prefs.denemopath, g_path_get_dirname (try));
                g_free (name);
                return try;
              }
            g_free (try);
        }
    }
  g_free (name);
  return NULL;
}

static gboolean printarea_visible = FALSE;
static void
hide_windows (void)
{
 
  if (Denemo.prefs.hide_windows)
    {
      if (Denemo.window)
        gtk_widget_hide (Denemo.window);
      if (Denemo.printarea)
        {
         printarea_visible = gtk_widget_get_visible(gtk_widget_get_toplevel (Denemo.printarea));
         gtk_widget_hide (gtk_widget_get_toplevel (Denemo.printarea));
       }
    }
}

static void
show_windows (void)
{
  if (Denemo.prefs.hide_windows)
    {
      if (Denemo.window)
        gtk_widget_show (Denemo.window);
      if (Denemo.printarea)
        {
          if (printarea_visible)
            gtk_widget_show (gtk_widget_get_toplevel (Denemo.printarea));
        }
    }
}

/**
 * Pops up a dialog box that has a text entry box and ok/cancel buttons
 * title is a title for the box.
 * initial_value is for the text entry box, or NULL if none.
 * instruction is a prompt for the user.
 * Returns a new value on Ok and NULL if cancelled.
 * The returned value should be freed by the caller.
 *
 */

gchar *
string_dialog_entry (DenemoProject * gui, gchar * title, gchar * instruction, gchar * initial_value)
{
  return string_dialog_entry_with_widget (gui, title, instruction, initial_value, NULL);
}

/* as string_dialog_entry() but with extra widget */
gchar *
string_dialog_entry_with_widget_opt (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gboolean modal)
{

  GtkWidget *dialog;
  GtkWidget *entry;
  GtkWidget *label;
  gchar *entry_string = NULL;
  //GString *string;
  entry = gtk_entry_new ();

  dialog = modal ? gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, _("_Cancel"), GTK_RESPONSE_REJECT, NULL) : gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, NULL);

  g_signal_connect (dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);      //if the user tries to dismiss the window, hide it, will that then hang??? It doesn't on GNOME using the right-click menu to close the window

  label = gtk_label_new (direction);
  GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  gtk_container_add (GTK_CONTAINER (content_area), label);

  if (widget)
    gtk_container_add (GTK_CONTAINER (content_area), widget);

  if (PreValue != NULL)
    {
      gtk_entry_set_text (GTK_ENTRY (entry), (gchar *) PreValue);
    }
  gtk_container_add (GTK_CONTAINER (content_area), entry);

  gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (Denemo.window));
  gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
  gtk_widget_show_all (dialog);

  if (modal)
    {
      gtk_widget_grab_focus (entry);
      hide_windows ();
      gtk_window_present (GTK_WINDOW (dialog));
      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
        {
          gchar *string = NULL;
          if (GTK_DIALOG (dialog))
            {
              entry_string = (gchar *) gtk_entry_get_text (GTK_ENTRY (entry));
              string = g_strdup (entry_string);
              gtk_widget_destroy (dialog);
            }
          show_windows ();
          return string;
        }
      else
        {
          if (GTK_DIALOG (dialog))
            gtk_widget_destroy (dialog);

        }
      show_windows ();
      return NULL;
    }
  else
    {
      g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_main_quit), entry);
      gtk_main ();
      gtk_window_present (GTK_WINDOW (dialog));
      if (GTK_IS_WIDGET (entry))
        {
          entry_string = GTK_IS_WIDGET (entry) ? g_strdup ((gchar *) gtk_entry_get_text (GTK_ENTRY (entry))) : NULL;
          gtk_widget_destroy (dialog);
          return entry_string;
        }
      return NULL;
    }
}

gchar *
string_dialog_entry_with_widget (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget)
{
  return string_dialog_entry_with_widget_opt (gui, wlabel, direction, PreValue, widget, TRUE);
}

/* as string_dialog_entry_with_widget() but gives a text editor instead of a single line editor */
gchar *
string_dialog_editor_with_widget_opt (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gboolean modal, gpointer keypress_callback)
{
  GtkWidget *dialog;
  GtkWidget *label;
  GtkTextTagTable *tagtable = (GtkTextTagTable *) gtk_text_tag_table_new ();
  GtkTextTag *t = gtk_text_tag_new ("ineditable");
  g_object_set (G_OBJECT (t), "background", "light gray", NULL);
  g_object_set (G_OBJECT (t), "editable", FALSE, NULL);
  gtk_text_tag_table_add (tagtable, t);
  t = gtk_text_tag_new ("code");
  g_object_set (G_OBJECT (t), "weight", PANGO_WEIGHT_BOLD, "family", "monospace", NULL);
  gtk_text_tag_table_add (tagtable, t);


  GtkTextBuffer *textbuffer = gtk_text_buffer_new (tagtable);
  GtkWidget *textview = gtk_text_view_new_with_buffer (textbuffer);

  gtk_text_buffer_set_text (textbuffer, PreValue ? PreValue : "", -1);
  GtkWidget *sw = gtk_scrolled_window_new (gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0), gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0));

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  dialog = modal ? gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, _("_Cancel"), GTK_RESPONSE_REJECT, NULL) : gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, NULL);
  if (keypress_callback)
    g_signal_connect (G_OBJECT (textview), "key-press-event", G_CALLBACK (keypress_callback), textbuffer);

  label = gtk_label_new (direction);
  GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  gtk_box_pack_start (GTK_BOX (content_area), label, FALSE, TRUE, 0);
  if (widget)
    {
      gtk_box_pack_start (GTK_BOX (content_area), widget, FALSE, TRUE, 0);
      g_object_set_data (G_OBJECT (widget), "textbuffer", textbuffer);
     // g_object_set_data (G_OBJECT (widget), "textview", textview);
    }
  gtk_container_add (GTK_CONTAINER (sw), textview);
  gtk_box_pack_start (GTK_BOX (content_area), sw, TRUE, TRUE, 0);

  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);


  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (Denemo.window));
  gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
  gtk_widget_show_all (dialog);
  gtk_widget_grab_focus (textview);
  if (modal)
    {
      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
        {
          GtkTextIter startiter, enditer;
          gtk_text_buffer_get_start_iter (textbuffer, &startiter);
          gtk_text_buffer_get_end_iter (textbuffer, &enditer);
          gchar *text = gtk_text_buffer_get_text (textbuffer, &startiter, &enditer, FALSE);
          gtk_widget_destroy (dialog);
          return text;
        }
      else
        {
          gtk_widget_destroy (dialog);
          return NULL;
        }
      return NULL;
    }
  else
    {
      g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_main_quit), NULL);
      gtk_main ();
      if (GTK_IS_TEXT_BUFFER (textbuffer))
        {
          GtkTextIter startiter, enditer;
          gtk_text_buffer_get_start_iter (textbuffer, &startiter);
          gtk_text_buffer_get_end_iter (textbuffer, &enditer);
          gchar *text = gtk_text_buffer_get_text (textbuffer, &startiter, &enditer, FALSE);
          gtk_widget_destroy (dialog);
          return text;
        }
      else
        {
          return NULL;
        }
    }
}

gchar *
string_dialog_editor_with_widget (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gpointer keypress_callback)
{
  return string_dialog_editor_with_widget_opt (gui, wlabel, direction, PreValue, widget, TRUE, keypress_callback);
}

gchar *get_multiline_input (gchar *title, gchar *instruction, gchar *initial)
{
    gchar *text = string_dialog_editor_with_widget (Denemo.project, title, instruction, initial, NULL, NULL);
#ifndef USE_EVINCE
#else
    drop_markup_area ();
#endif
    return text;
}
static gboolean
option_choice (GtkWidget * widget, gchar ** response)
{
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    *response = g_object_get_data (G_OBJECT (widget), "choice");
  //do not respond to the toggling off of other radio buttons
  return TRUE;
}

static gint root_x;
static gint root_y;
static gboolean
dialog_realize (GtkWidget * dialog)
{
  if (root_x)
    gtk_window_move (GTK_WINDOW (dialog), root_x, root_y);
  return FALSE;
}

/* run a dialog for the user to select a string from the NULL separated strings, str
 return NULL if user cancels.*/
static gchar *
get_option_recursive (gchar * title, gchar * str, gint length, gboolean more)
{
  gchar *response = NULL;
  if (title == NULL)
    title = _("Select from List (or Cancel)");
  GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
                                                   GTK_WINDOW (Denemo.window),
                                                   (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                                   _("_OK"), GTK_RESPONSE_ACCEPT,
                                                   _("_Cancel"), GTK_RESPONSE_REJECT,
                                                   NULL);
  gtk_widget_set_size_request (dialog, strlen (title) * 10, -1);
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
  g_signal_connect_after (G_OBJECT (dialog), "realize", G_CALLBACK (dialog_realize), NULL);
  GtkWidget *vbox = gtk_vbox_new (FALSE, 1);
  GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  gtk_container_add (GTK_CONTAINER (content_area), vbox);

  gchar *opt;
  GtkWidget *widget1, *widget;
  for (opt = str; (opt - str) < length; opt += strlen (opt) + 1)
    {
      if (opt == str)
        {
          widget = widget1 = gtk_radio_button_new_with_label (NULL, opt);
          response = str;       //this is the first radio button, we set response to be the input string ie first option, in case the user makes no choice, in which case the callback option_choice is not called.
        }
      else
        {
          widget = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (widget1), opt);
        }
      g_object_set_data (G_OBJECT (widget), "choice", (gpointer) opt);
      g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (option_choice), &response);
      gtk_container_add (GTK_CONTAINER (vbox), widget);
    }

  if (more)
    {
      opt = _("More...");
      widget = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (widget1), opt);
      g_object_set_data (G_OBJECT (widget), "choice", (gpointer) opt);
      g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (option_choice), &response);
      gtk_container_add (GTK_CONTAINER (vbox), widget);
    }
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (Denemo.window));
  gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
  gtk_widget_show_all (dialog);
  hide_windows ();
  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_REJECT)
    {
      response = NULL;
    }
  g_debug ("Returning contents of response is %s\n", response);
  gtk_window_get_position (GTK_WINDOW (dialog), &root_x, &root_y);
  gtk_widget_destroy (dialog);
  show_windows ();
  return response;
}

#define MAX_ITEMS (Denemo.prefs.max_menu_size)
gchar *
get_option (gchar * title, gchar * str, gint length)
{
  gchar *opt;
  gint i;
  for (opt = str, i = 0; (opt - str) < length; i++, opt += strlen (opt) + 1)
    {
      if (i < MAX_ITEMS)
        continue;
      else
        {
          gchar *response = get_option_recursive (title, str, opt - str, TRUE);
          if (response && (!strcmp (response, _("More..."))))
            {
              length -= (opt - str);
              str = opt;
              i = 0;
              continue;
            }
          else
            return response;
        }
    }
  if ((opt - str) >= length)
    return get_option_recursive (title, str, length, FALSE);
  return NULL;
}




/* output text to the console (lilypond errors) window. If text==NULL clear window*/
void
console_output (gchar * text)
{
  GtkTextIter enditer;
  GtkTextBuffer *buffer = gtk_text_view_get_buffer ((GtkTextView *) (Denemo.console));
  gtk_text_buffer_get_end_iter (buffer, &enditer);
  if (text)
    gtk_text_buffer_insert (buffer, &enditer, text, -1);
  else
    gtk_text_buffer_set_text (buffer, "", -1);
}

/* returns an override flag ORd from all those in the list of directives,
   excluding ones with DENEMO_OVERRIDE_HIDDEN set and ones not for the current layout */
gint
get_override (GList * g)
{
  gint ret = 0;
  for (; g; g = g->next)
    {
      DenemoDirective *d = g->data;
      if (wrong_layout (d, Denemo.project->layout_id))
        continue;
      if (!(d->override & DENEMO_OVERRIDE_HIDDEN))
        ret |= d->override;
    }
  return ret;
}

//modfies name to truncate at the extension (a dot at end before any directory separators), returning a pointer to the extension chopped off
// returns NULL if no extension, with name unchanged

gchar *
remove_extension (gchar * name)
{
  gchar *c;
  if (name)
    for (c = name + strlen (name); c != name; c--)
      {
        if (*c == '.')
          {
            *c = '\0';
            return c + 1;
          }
        if (*c == G_DIR_SEPARATOR)
          break;
      }
  return NULL;
}

//modifies name, removing the extension and returns a newly allocated string
//with the passed extension
gchar *
substitute_extension (gchar * name, gchar * extension)
{
  (void) remove_extension (name);
  return g_strdup_printf ("%s.%s", name, extension);
}

enum clefs
cleftypefromname (gchar * str)
{
  enum clefs ret = DENEMO_TREBLE_CLEF;

  if (g_strcasecmp (str, "treble") == 0)
    ret = DENEMO_TREBLE_CLEF;
  else if (g_strcasecmp (str, "bass") == 0)
    ret = DENEMO_BASS_CLEF;
  else if (g_strcasecmp (str, "alto") == 0)
    ret = DENEMO_ALTO_CLEF;
  else if (g_strcasecmp (str, "\"g_8\"") == 0)
    ret = DENEMO_G_8_CLEF;
  else if (g_strcasecmp (str, "tenor") == 0)
    ret = DENEMO_TENOR_CLEF;
  else if (g_strcasecmp (str, "soprano") == 0)
    ret = DENEMO_SOPRANO_CLEF;
  g_free (str);
  return ret;
}

gint
get_widget_height (GtkWidget * w)
{
  GtkAllocation allocation;
  gtk_widget_get_allocation (w, &allocation);
  return allocation.height;
}

gint
get_widget_width (GtkWidget * w)
{
  GtkAllocation allocation;
  gtk_widget_get_allocation (w, &allocation);
  return allocation.width;
}

void
switch_back_to_main_window (void)
{
  if (Denemo.prefs.noswitchback)
	return;
  if (Denemo.non_interactive)
    return;
  gtk_window_present (GTK_WINDOW (Denemo.window));
  gtk_widget_grab_focus (Denemo.scorearea);
}

//callback for keypress event on various windows that Denemo creates to close it with Esc etc
gboolean window_keypress_event (GtkWidget * widget, GdkEventKey * event)
{
  //g_print ("window key press event: keyval %d (%s), string |%s|, length %d, state %x, keycode %d, group %d, is_modifier flag %d\n", event->keyval, gdk_keyval_name(event->keyval), event->string, event->length, event->state, event->hardware_keycode, event->group, event->is_modifier);
  if ((event->keyval == 65307) || ((event->keyval == 'w') && (event->state == GDK_CONTROL_MASK))) //Esc OR ctrl-w
	{
		switch_back_to_main_window ();
		return TRUE;
	}
  return FALSE;
}

/* set all labels in the hierarchy below widget to use markup */
void
use_markup (GtkWidget * widget)
{
  if (!widget)
    return;
  if (GTK_IS_LABEL (widget))
    {
      gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
    }
  else if (GTK_IS_CONTAINER (widget))
    {
      GList *g = gtk_container_get_children (GTK_CONTAINER (widget));
      for (; g; g = g->next)
        use_markup (g->data);
      if (GTK_IS_MENU_ITEM (widget))
        {
          use_markup (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)));
        }
    }
}

#ifdef FAKE_TOOLTIPS
static GtkWidget *TooltipPopup = NULL;
static guint hide_tooltip_timeout_id = 0;

// Hide and destroy the tooltip window
static void
hide_tooltip_window (void)
{
  if (TooltipPopup != NULL)
    {
      gtk_widget_destroy (TooltipPopup);
      TooltipPopup = NULL;
    }
  if (hide_tooltip_timeout_id > 0)
    {
      g_source_remove (hide_tooltip_timeout_id);
      hide_tooltip_timeout_id = 0;
    }
}

// Timeout callback to auto-hide tooltip
static gboolean
tooltip_auto_hide (gpointer data)
{
  hide_tooltip_window ();
  hide_tooltip_timeout_id = 0;
  return FALSE;  // Remove timeout
}

gchar *format_tooltip (const gchar *tip) {
    gchar *this = (gchar*)tip;
    if (tip==NULL)
        return _("No Tooltip");
    GString *out = g_string_new ("");
    for (this = (gchar*)tip; *this; this++)
        {
        if (*this == '.')  //UTF8 strings never contain ASCII bytes except to represent the corresponding ASCII char
            {
                this++;
                if (*this == ' ')
                    g_string_append (out, ".\n");
                else
                    {
                        this--;
                        g_string_append_c (out, *this);
                    }
            }
        else
            g_string_append_c (out, *this);
        
    }
    return g_string_free (out, FALSE);
}    

    
gboolean
show_tooltip (GtkWidget * w, GdkEvent * ev, gchar * text)
{
  static gchar *last_tooltip;
  static guint32 last_time;
  static GtkWidget *last_widget;
  static gint last_y;
  guint32 time;
  gdouble x, y;

  if (w == NULL)
    {
      last_widget = NULL;
      last_y = last_time = 0;
      hide_tooltip_window ();
      return FALSE;
    }

  time = gdk_event_get_time (ev);
  gdk_event_get_coords (ev, &x, &y);

  if (w != last_widget)
    {
      last_widget = w;
      last_time = time;
      hide_tooltip_window ();  // Hide any existing tooltip when moving to new widget
      return FALSE;
    }

  if (abs (last_time - time) < Denemo.prefs.tooltip_timeout)
    {
      last_y = (int)y;
      return FALSE;
    }

  if (abs (last_y - (int)y) > 10)
    {
      last_y = (int)y;
      hide_tooltip_window ();  // Hide on vertical movement
      return FALSE;
    }

  if (last_tooltip != text)
    if (text && (*text) && (last_tooltip != text))
      {
        last_tooltip = text;
        hide_tooltip_window ();  // Hide old tooltip first

        // Create a lightweight popup window (no decorations, no grab!)
        TooltipPopup = gtk_window_new (GTK_WINDOW_POPUP);
        gtk_window_set_type_hint (GTK_WINDOW (TooltipPopup), GDK_WINDOW_TYPE_HINT_TOOLTIP);
        gtk_window_set_resizable (GTK_WINDOW (TooltipPopup), FALSE);

        // Enable visual transparency for rounded corners
        GdkScreen *screen = gtk_widget_get_screen (TooltipPopup);
        GdkVisual *visual = gdk_screen_get_rgba_visual (screen);
        if (visual)
          gtk_widget_set_visual (TooltipPopup, visual);

        // Create label with tooltip text
        GtkWidget *label = gtk_label_new (text);
        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
        gtk_label_set_max_width_chars (GTK_LABEL (label), 80);
        gtk_widget_set_margin_start (label, 8);
        gtk_widget_set_margin_end (label, 8);
        gtk_widget_set_margin_top (label, 6);
        gtk_widget_set_margin_bottom (label, 6);

        gtk_container_add (GTK_CONTAINER (TooltipPopup), label);

        // Style it to look like a tooltip (light yellow background, rounded corners)
        GtkCssProvider *css_provider = gtk_css_provider_new ();
        gtk_css_provider_load_from_data (css_provider,
          "window { "
          "  background-color: rgba(255, 255, 204, 0.98); "  /* Slight transparency */
          "  border: 1px solid #888888; "
          "  border-radius: 6px; "
          "}"
          "label { "
          "  color: #000000; "
          "  font-size: 9pt; "
          "}",
          -1, NULL);
        gtk_style_context_add_provider (gtk_widget_get_style_context (TooltipPopup),
                                        GTK_STYLE_PROVIDER (css_provider),
                                        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
        g_object_unref (css_provider);

        // Position near mouse cursor
        gint mouse_x, mouse_y;
        GdkDisplay *display = gdk_display_get_default ();
        GdkSeat *seat = gdk_display_get_default_seat (display);
        GdkDevice *pointer = gdk_seat_get_pointer (seat);
        gdk_device_get_position (pointer, NULL, &mouse_x, &mouse_y);

        gtk_window_move (GTK_WINDOW (TooltipPopup), mouse_x + 10, mouse_y + 15);
        gtk_widget_show_all (TooltipPopup);

        // Auto-hide after 30 seconds to prevent permanent tooltips
        if (hide_tooltip_timeout_id > 0)
          g_source_remove (hide_tooltip_timeout_id);
        hide_tooltip_timeout_id = g_timeout_add (30000, tooltip_auto_hide, NULL);

        return FALSE;
      }
  return FALSE;  // Allow normal response
}

void
free_tooltip (GtkWidget * w, gchar * tooltip)
{
  // When widget is destroyed, hide any active tooltip
  hide_tooltip_window ();
  g_free (tooltip);
}
#endif
// Help for beginners using keyboard shortcuts
static GtkWidget *KeyStrokes;
static GtkWidget *KeyStrokeLabel;
static GtkWidget *KeyStrokeHelp;


void
KeyStrokeAwait (gchar * first_keypress)
{
  KeyStrokeShow (first_keypress, DENEMO_NO_COMMAND, TwoKey);
}

void
KeyStrokeDecline (gchar * first_keypress)
{
  KeyStrokeShow (first_keypress, DENEMO_NO_COMMAND, SingleKey);
}

void
MouseGestureShow (gchar * str, gchar * help, DenemoShortcutType type)
{
  gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), help);
  KeyStrokeShow (str, DENEMO_NO_COMMAND, type);
}

void
KeyPlusMouseGestureShow (gchar * str, gint command_idx)
{
  const gchar *label = lookup_label_from_idx (Denemo.map, command_idx);
  const gchar *tooltip = lookup_tooltip_from_idx (Denemo.map, command_idx);
  gchar *text = g_strdup_printf (_("Mouse shortcut <span font_desc=\"24\" bgcolor=\"white\" face=\"mono\" foreground=\"blue\">%s</span>" " invokes command <span font_desc=\"24\" foreground=\"dark red\">%s</span>"), str, label);

  gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Mouse Shortcut"));
  gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text);
  gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), tooltip);
  g_free (text);
  gtk_widget_show (KeyStrokes);
}

void
KeyStrokeShow (gchar * str, gint command_idx, DenemoShortcutType type)
{
  if (!g_strcmp0 (str, "0xffffff")) return;//strange value seen sometimes (on Windows?) when the OS intercepts only part of a keypress?
	
  if (str != NULL)
    {
      str = g_strdup_printf ("%s", str);
      gchar *c;
      for (c=str;*c;c++)
        {
          if (*c=='<' || *c=='>')
            *c = '_';
        }
      gchar *text;
      if (command_idx != DENEMO_NO_COMMAND)
        {
          const gchar *label = lookup_label_from_idx (Denemo.map, command_idx);
          const gchar *tooltip = lookup_tooltip_from_idx (Denemo.map, command_idx);
          if (type)
            {
              text = g_strdup_printf (_("Key Press <span font_desc=\"24\" bgcolor=\"white\" face=\"mono\" foreground=\"blue\">%s</span>" " invokes command <span font_desc=\"24\" foreground=\"dark red\">%s</span>"), str, label);

            }
          else
            {
              text = g_strdup_printf (_("Key Presses <span font_desc=\"24\"  bgcolor=\"white\" face=\"mono\" foreground=\"blue\">%s</span>" " invoke command <span font_desc=\"24\" foreground=\"dark red\">%s</span>"), str, label);
            }
          gtk_window_set_title (GTK_WINDOW (KeyStrokes), type == SingleKey ? _("Single Key Press") : _("Two Key Presses"));
          gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text);
          gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), tooltip);
          g_free (text);
        }
      else                      // no command
        {
          switch (type)
            {
            case SingleKey:
              gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Key Press"));
              gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), "");
              text = g_strdup_printf (_("Key Press <span font_desc=\"24\"  bgcolor=\"white\" face=\"mono\" foreground=\"blue\">%s</span>" " Is not a shortcut.\n%s"), str, (Denemo.project->view != DENEMO_MENU_VIEW) ? _("(The menus are now restored in case you are lost.)") : "");
              break;
            case TwoKey:
              gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("First Key Press"));
              gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), "");
              text = g_strdup_printf (_("Key Press <span font_desc=\"24\"  bgcolor=\"white\" face=\"mono\" foreground=\"blue\">%s</span>" " Awaiting continuation"), str);
              break;
            case MouseGesture:
              gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Mouse"));
              text = g_strdup_printf (_("Mouse <span font_desc=\"24\"  bgcolor=\"white\" face=\"mono\" foreground=\"blue\">%s</span>"), str);
              break;
            case KeyPlusMouse:
              gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Key + Mouse"));
              text = g_strdup_printf (_("Key Press <span font_desc=\"24\"  bgcolor=\"white\" face=\"mono\" foreground=\"blue\">%s</span>"), str);
              break;
            default:
              g_warning ("bad call");
              return;
            }
          gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text);
          g_free (text);
        }
    }
  gtk_widget_show (KeyStrokes);
}

static gboolean
toggle_show_keystroke_preference (void)
{
  Denemo.prefs.learning = FALSE;
  gtk_widget_hide (KeyStrokes);
  return TRUE;
}

void
initialize_keystroke_help (void)
{
  if (KeyStrokes == NULL)
    {
      KeyStrokes = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      g_signal_connect (G_OBJECT (KeyStrokes), "delete-event", G_CALLBACK (toggle_show_keystroke_preference), NULL);
      gtk_window_set_transient_for (GTK_WINDOW (KeyStrokes), GTK_WINDOW (Denemo.window));
      gtk_window_set_keep_above (GTK_WINDOW (KeyStrokes), TRUE);
      gtk_window_set_accept_focus (GTK_WINDOW (KeyStrokes), FALSE);
      KeyStrokeLabel = gtk_label_new ("");
      //gtk_label_set_line_wrap (KeyStrokeLabel, TRUE);
      KeyStrokeHelp = gtk_label_new ("");
      gtk_label_set_line_wrap (GTK_LABEL (KeyStrokeHelp), TRUE);
      GtkWidget *vbox = gtk_vbox_new (FALSE, 8);
      gtk_container_add (GTK_CONTAINER (KeyStrokes), vbox);
      gtk_box_pack_start (GTK_BOX (vbox), KeyStrokeLabel, FALSE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (vbox), KeyStrokeHelp, FALSE, TRUE, 0);
      gtk_label_set_use_markup (GTK_LABEL (KeyStrokeLabel), TRUE);
      gtk_widget_show_all (KeyStrokes);
    }
  gtk_widget_hide (KeyStrokes);
}

void init_gdk_cursors (void) {
    Denemo.GDK_LEFT_PTR = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_LEFT_PTR);
    Denemo.GDK_SB_V_DOUBLE_ARROW = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_SB_V_DOUBLE_ARROW);
    Denemo.GDK_SB_H_DOUBLE_ARROW = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_SB_H_DOUBLE_ARROW);
    Denemo.GDK_X_CURSOR = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_X_CURSOR);
    Denemo.GDK_BLANK_CURSOR = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_BLANK_CURSOR);
    Denemo.GDK_TARGET = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_TARGET);
    Denemo.GDK_CIRCLE = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_CIRCLE);
}
/**
 * find_dir_for_file:
 * @filename: The file to search
 * @dirs: A dir paths array, ending by NULL, where to search.
 *
 * Finds the first dir in the list that contains 'filename', and free the array.
 *
 * Returns: The dir path if found, NULL either
 **/
gchar *
find_dir_for_file (gchar * filename, GList * dirs)
{
  gchar *dir = NULL;
  gchar *path = NULL;
  GList *curdir = NULL;

  for (curdir = dirs; curdir; curdir = g_list_next (curdir))
    {
      if (!dir)
        {
          path = g_build_filename (curdir->data, filename, NULL);
          if (g_file_test (path, G_FILE_TEST_EXISTS))
            dir = g_strdup (curdir->data);      //even though found, the loop is continued to clear the list of dirs
          // else here causes a memory leak as path is not used hereafter
          g_free (path);
        }
      g_free (curdir->data);
    }
  return dir;
}

/**
 * find_dir_for_files:
 * @files: The files to search
 * @dirs: A dir paths list, where to search.
 *
 * Finds the first dir in the list that contains 'filename', and free the array.
 *
 * Returns: The dir path if found, NULL either
 **/
gchar *
find_dir_for_files (GList * files, GList * dirs)
{
  gchar *dir = NULL;
  gchar *path = NULL;
  GList *curdir = NULL;
  GList *curfile = NULL;

  for (curdir = dirs; curdir; curdir = g_list_next (curdir))
    {
      for (curfile = files; curfile; curfile = g_list_next (curfile))
        {
          if (!dir)
            {
              path = g_build_filename (curdir->data, curfile->data, NULL);
              if (g_file_test (path, G_FILE_TEST_EXISTS))
                dir = g_strdup (curdir->data);
              g_free (path);
            }
        }
      g_free (curdir->data);
    }
  return dir;
}

/**
 * find_path_for_file:
 * @filename: The file to search
 * @dirs: A dir paths list, where to search.
 *
 * Finds the first dir in the list that contains 'filename', and free the array.
 *
 * Returns: The file path if found, NULL either
 **/
gchar *
find_path_for_file (gchar * filename, GList * dirs)
{
  gchar *dir = find_dir_for_file (filename, dirs);
  if (dir)
    {
      gchar *path = g_build_filename (dir, filename, NULL);
      g_free (dir);
      return path;
    }
  return NULL;
}

gchar *
get_project_dir (void)
{
  if (Denemo.project && Denemo.project->filename->len)
    {
      return g_path_get_dirname (Denemo.project->filename->str);
    }
  return g_strdup (g_get_home_dir ());
}

gchar *
get_local_dir (DenemoDirectory dir)
{
  switch (dir)
    {
    case DENEMO_DIR_COMMANDS:
      return COMMANDS_DIR;
    case DENEMO_DIR_UI:
      return UI_DIR;
    case DENEMO_DIR_SOUNDFONTS:
      return SOUNDFONTS_DIR;
    case DENEMO_DIR_PIXMAPS:
      return PIXMAPS_DIR;
    case DENEMO_DIR_FONTS:
      return FONTS_DIR;
    case DENEMO_DIR_LOCALE:
      return LOCALE_DIR;
    case DENEMO_DIR_LILYPOND_INCLUDE:
      return Denemo.lilypond_include_dir;
    default:
      return NULL;
    }
}

gchar *
get_system_dir (DenemoDirectory dir)
{
  switch (dir)
    {
    case DENEMO_DIR_COMMANDS:
    case DENEMO_DIR_UI:
    case DENEMO_DIR_SOUNDFONTS:
    case DENEMO_DIR_FONTS:
    case DENEMO_DIR_LILYPOND_INCLUDE:
      return g_build_filename (get_system_data_dir (), get_local_dir (dir), NULL);
    case DENEMO_DIR_PIXMAPS:
      return g_build_filename (get_system_data_dir (), PIXMAPS_DIR, NULL);
      break;
    case DENEMO_DIR_LOCALE:
      return g_strdup (get_system_locale_dir ());
      break;
    case DENEMO_DIR_BIN:
      return g_strdup (get_system_bin_dir ());
      break;
    default:
      return NULL;
    }
}

const gchar *
get_executable_dir ()
{
  static const gchar *dir = NULL;
  if (dir == NULL)
    {
      char path[1024];
      gint n;
#ifdef G_OS_WIN32
      GetModuleFileNameW (NULL, path, MAX_PATH);

#elif defined _MACH_O_
      guint size = sizeof (path);
      _NSGetExecutablePath (path, &size);

#else
      if ((n = readlink ("/proc/self/exe", path, sizeof (path))) < 0)
        {
          perror ("/proc/self/exe");
          return NULL;
        }
      path[n] = 0;
#endif
      dir = g_path_get_dirname (path);
    }
  return dir;
}

/**
 * find:
 * @dir: The denemo directory where to search
 * @filename: The file to search
 *
 * Finds a file by searching:
 *  - in the local directory
 *  - in the executable parent directory
 *  - in the user directory
 *  - in the system directory
 *  - in the system fonts directory
 **/
gchar *
find_denemo_file (DenemoDirectory dir, gchar * filename)
{
  //g_debug("find_denemo_file called with %d and %s\n", dir, filename);
  GList *dirs = NULL;
  dirs = g_list_append (dirs, g_build_filename (PACKAGE_SOURCE_DIR, get_local_dir (dir), NULL));
  dirs = g_list_append (dirs, g_build_filename (get_executable_dir (), "..", get_local_dir (dir), NULL));
  dirs = g_list_append (dirs, g_build_filename (get_user_data_dir (TRUE), get_local_dir (dir), NULL));
  dirs = g_list_append (dirs, g_strdup (get_system_dir (dir)));
  dirs = g_list_append (dirs, g_build_filename (get_executable_dir (), "..", "share", "fonts", "truetype", "denemo", NULL));
  return find_path_for_file (filename, dirs);
}

gchar *
escape_scheme (gchar * input)
{
  gchar *c = input - 1;
  GString *out = g_string_new ("");
  for (c = input; c && *c; c++)
    {
      if (*c == '"')
        g_string_append (out, "\\\\\\\"");
      else if (*c == '\\')
        g_string_append (out, "\\\\");
      else
        {
          g_string_append_printf (out, "%c", *c);
        }
    }
  return g_string_free (out, FALSE);
}

gboolean modifier_held_down (gint modifier)
{
  GdkModifierType mask;
  GdkWindow *win = gtk_widget_get_window (Denemo.window);
#if GTK_MAJOR_VERSION == 2
  gdk_window_get_pointer (win, NULL, NULL, &mask);
#else

#if GTK_MINOR_VERSION < 20
  gdk_window_get_device_position (win, gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_display_get_default ())), NULL, NULL, &mask);
#else
  gdk_window_get_device_position (win, gdk_seat_get_pointer (gdk_display_get_default_seat (gdk_display_get_default ())), NULL, NULL, &mask);
#endif

#endif
  return (mask & modifier);
}

gboolean shift_held_down (void)
{
  return modifier_held_down (GDK_SHIFT_MASK);
}

gboolean control_held_down (void)
{
  return modifier_held_down (GDK_CONTROL_MASK);
}



#if GTK_MAJOR_VERSION == 2
#define GdkRGBA GdkColor
#define gtk_widget_override_color gtk_widget_modify_fg
#define gtk_widget_override_background_color gtk_widget_modify_bg
#define GTK_STATE_FLAG_NORMAL (0)
void
get_color (GdkColor * color, gdouble r, gdouble g, gdouble b, gdouble a)
{
  gchar *col = g_strdup_printf ("#%02x%02x%02x", (gint) (r * 254), (gint) (g * 254), (gint) (b * 254));
  gdk_color_parse (col, color);
  g_free (col);
}
#else
void
get_color (GdkRGBA * color, gdouble r, gdouble g, gdouble b, gdouble a)
{
  color->red = r;
  color->green = g;
  color->blue = b;
  color->alpha = a;
}

static gchar *get_css_selector (GtkWidget *w)
{
static gchar *ret = NULL;

#if ((GTK_MAJOR_VERSION==3) && (GTK_MINOR_VERSION>=20))
  g_free (ret);
  ret = g_ascii_strdown (g_strdup (g_type_name (G_TYPE_FROM_INSTANCE (w))), -1);
  return ret+3;
#else
  ret = (gchar *)g_type_name (G_TYPE_FROM_INSTANCE (w));
  return ret;
#endif
}

void set_background_color(GtkWidget *w, gchar *color)
{
GtkCssProvider *gcp;
GtkStyleContext *gsc;
gsc = gtk_widget_get_style_context(w);
gchar *type = get_css_selector(w);
gchar *str = g_strdup_printf ("%s {background-color: %s;}", type, color); //g_print ("%s", str);
gcp= gtk_css_provider_new();
gtk_css_provider_load_from_data(gcp, str, -1, 0);
g_free (str);
gtk_style_context_add_provider(gsc, GTK_STYLE_PROVIDER(gcp), 
    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}

void set_foreground_color(GtkWidget *w, gchar *color)
{
GtkCssProvider *gcp;
GtkStyleContext *gsc;
gsc = gtk_widget_get_style_context(w);
gchar *type = get_css_selector(w);
gchar *str = g_strdup_printf ("%s {color: %s;}", type, color); //g_print ("%s", str);
gcp= gtk_css_provider_new();
gtk_css_provider_load_from_data(gcp, str, -1, 0);
g_free (str);
gtk_style_context_add_provider(gsc, GTK_STYLE_PROVIDER(gcp), 
    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}

#endif
