/* Dia -- an diagram creation/manipulation program
 * Copyright (C) 1998 Alexander Larsson *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <config.h>

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h>
#include <glib.h>

#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gtk/gtk.h>

 #ifdef GNOME 	 
 #  include <gnome.h> 	 
 #endif 	 

#ifdef G_OS_WIN32
/*
 * Instead of polluting the Dia namespace with windoze headers, declare the
 * required prototype here. This is bad style, but not as bad as namespace
 * clashes to be resolved without C++   --hb
 */
long __stdcall
ShellExecuteA (long        hwnd,
               const char* lpOperation,
               const char* lpFile,
               const char* lpParameters,
               const char* lpDirectory,
               int         nShowCmd);
#endif

#include "paginate_psprint.h"
#ifdef G_OS_WIN32
#  include "paginate_gdiprint.h"
#endif
#include "intl.h"
#include "commands.h"
#include "app_procs.h"
#include "diagram.h"
#include "display.h"
#include "object_ops.h"
#include "cut_n_paste.h"
#include "load_save.h"
#include "utils.h"
#include "message.h"
#include "grid.h"
#include "properties.h"
#include "propinternals.h"
#include "preferences.h"
#include "layer_dialog.h"
#include "connectionpoint_ops.h"
#include "undo.h"
#include "pagesetup.h"
#include "text.h"
#include "dia_dirs.h"
#include "focus.h"
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include "lib/properties.h"
#include "lib/parent.h"
#include "dia-props.h"
#include "diagram_tree_window.h"
#include "authors.h"                /* master contributors data */

void 
file_quit_callback (GtkAction *action)
{
  app_exit();
}

void
file_pagesetup_callback (GtkAction *action)
{
  Diagram *dia;

  dia = ddisplay_active_diagram();
  if (!dia) return;
  create_page_setup_dlg(dia);
}

void
file_print_callback (GtkAction *action)
{
  Diagram *dia;

  dia = ddisplay_active_diagram();
  if (!dia) return;
#ifdef G_OS_WIN32
  /* This option could be used with Gnome too. Does it make sense there ? */
  if (!prefs.prefer_psprint)
    diagram_print_gdi(dia);
  else
#endif
    diagram_print_ps(dia);
}

void
file_close_callback (GtkAction *action)
{
  /* some people use tear-off menus and insist to close non existing displays */
  if (ddisplay_active())
    ddisplay_close(ddisplay_active());
}

void
file_new_callback (GtkAction *action)
{
  Diagram *dia;
  DDisplay *ddisp;
  static int untitled_nr = 1;
  gchar *name, *filename;

  name = g_strdup_printf(_("Diagram%d.dia"), untitled_nr++);
  filename = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
  dia = new_diagram(filename);
  ddisp = new_display(dia);
  diagram_tree_add(diagram_tree(), dia);
  g_free (name);
  g_free (filename);
}

void
file_preferences_callback (GtkAction *action)
{
  prefs_show();
}


void
edit_copy_callback (GtkAction *action)
{
  GList *copy_list;
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  copy_list = parent_list_affected(diagram_get_sorted_selected(ddisp->diagram));

  cnp_store_objects(object_copy_list(copy_list), 1);
  g_list_free(copy_list);

  ddisplay_do_update_menu_sensitivity(ddisp);
}

void
edit_cut_callback (GtkAction *action)
{
  GList *cut_list;
  DDisplay *ddisp;
  Change *change;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  diagram_selected_break_external(ddisp->diagram);

  cut_list = parent_list_affected(diagram_get_sorted_selected(ddisp->diagram));

  cnp_store_objects(object_copy_list(cut_list), 0);

  change = undo_delete_objects_children(ddisp->diagram, cut_list);
  (change->apply)(change, ddisp->diagram);
  
  ddisplay_do_update_menu_sensitivity(ddisp);
  diagram_flush(ddisp->diagram);

  diagram_modified(ddisp->diagram);
  diagram_update_extents(ddisp->diagram);
  undo_set_transactionpoint(ddisp->diagram->undo);
}

void
edit_paste_callback (GtkAction *action)
{
  GList *paste_list;
  DDisplay *ddisp;
  Point paste_corner;
  Point delta;
  Change *change;
  int generation = 0;
  
  ddisp = ddisplay_active();
  if (!ddisp) return;

  if (!cnp_exist_stored_objects()) {
    message_warning(_("No existing object to paste.\n"));
    return;
  }
  
  paste_list = cnp_get_stored_objects(&generation); /* Gets a copy */

  paste_corner = object_list_corner(paste_list);
  
  delta.x = ddisp->visible.left - paste_corner.x;
  delta.y = ddisp->visible.top - paste_corner.y;

  /* Move down some 10% of the visible area. */
  delta.x += (ddisp->visible.right - ddisp->visible.left) * 0.1 * generation;
  delta.y += (ddisp->visible.bottom - ddisp->visible.top) * 0.1 * generation;

  if (generation)
    object_list_move_delta(paste_list, &delta);

  change = undo_insert_objects(ddisp->diagram, paste_list, 0);
  (change->apply)(change, ddisp->diagram);

  diagram_modified(ddisp->diagram);
  undo_set_transactionpoint(ddisp->diagram->undo);
  
  diagram_remove_all_selected(ddisp->diagram, TRUE);
  diagram_select_list(ddisp->diagram, paste_list);

  diagram_update_extents(ddisp->diagram);
  diagram_flush(ddisp->diagram);
}

/*
 * ALAN: Paste should probably paste to different position, feels
 * wrong somehow.  ALAN: The offset should increase a little each time
 * if you paste/duplicate several times in a row, because it is
 * clearer what is happening than if you were to piling them all in
 * one place.
 *
 * completely untested, basically it is copy+paste munged together
 */
void
edit_duplicate_callback (GtkAction *action)
{ 
  GList *duplicate_list;
  DDisplay *ddisp;
  Point duplicate_corner;
  Point delta;
  Change *change;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  duplicate_list = object_copy_list(diagram_get_sorted_selected(ddisp->diagram));
  duplicate_corner = object_list_corner(duplicate_list);
  
  /* Move down some 10% of the visible area. */
  delta.x = (ddisp->visible.right - ddisp->visible.left)*0.05;
  delta.y = (ddisp->visible.bottom - ddisp->visible.top)*0.05;

  object_list_move_delta(duplicate_list, &delta);

  change = undo_insert_objects(ddisp->diagram, duplicate_list, 0);
  (change->apply)(change, ddisp->diagram);

  diagram_modified(ddisp->diagram);
  undo_set_transactionpoint(ddisp->diagram->undo);
  
  diagram_remove_all_selected(ddisp->diagram, TRUE);
  diagram_select_list(ddisp->diagram, duplicate_list);

  diagram_flush(ddisp->diagram);
  
  ddisplay_do_update_menu_sensitivity(ddisp);
}




/* Signal handler for getting the clipboard contents */
/* Note that the clipboard is for M$-style cut/copy/paste copying, while
   the selection is for Unix-style mark-and-copy.  We can't really do
   mark-and-copy.
*/

static void
insert_text(DDisplay *ddisp, Focus *focus, const gchar *text)
{
  ObjectChange *change = NULL;
  int modified = FALSE, any_modified = FALSE;
  DiaObject *obj = focus_get_object(focus);

  while (text != NULL) {
    gchar *next_line = g_utf8_strchr(text, -1, '\n');
    if (next_line != text) {
      gint len = g_utf8_strlen(text, (next_line-text));
      modified = (*focus->key_event)(focus, GDK_A, text, len, &change);
    }
    if (next_line != NULL) {
      modified = (*focus->key_event)(focus, GDK_Return, "\n", 1, &change);
      text = g_utf8_next_char(next_line);
    } else {
      text = NULL;
    }
    { /* Make sure object updates its data: */
      Point p = obj->position;
      (obj->ops->move)(obj,&p);  }

    /* Perhaps this can be improved */
    object_add_updates(obj, ddisp->diagram);
    
    if (modified && (change != NULL)) {
      undo_object_change(ddisp->diagram, obj, change);
      any_modified = TRUE;
    }
    
    diagram_flush(ddisp->diagram);
  }

  if (any_modified) {
    diagram_modified(ddisp->diagram);
    undo_set_transactionpoint(ddisp->diagram->undo);
  }
}


static void
received_clipboard_handler(GtkClipboard *clipboard, 
			   const gchar *text,
			   gpointer data) {
  Focus *focus = active_focus();
  DDisplay *ddisp = (DDisplay *)data;
  
  if (text == NULL) return;

  if ((focus == NULL) || (!focus->has_focus)) return;

  if (!g_utf8_validate(text, -1, NULL)) {
    message_error("Not valid UTF8");
    return;
  }

  insert_text(ddisp, focus, text);
}

static PropDescription text_prop_singleton_desc[] = {
    { "text", PROP_TYPE_TEXT },
    PROP_DESC_END};

static void 
make_text_prop_singleton(GPtrArray **props, TextProperty **prop) 
{
  *props = prop_list_from_descs(text_prop_singleton_desc,pdtpp_true);
  g_assert((*props)->len == 1);

  *prop = g_ptr_array_index((*props),0);
  g_free((*prop)->text_data);
  (*prop)->text_data = NULL;
}


void
edit_copy_text_callback (GtkAction *action)
{
  Focus *focus = active_focus();
  DDisplay *ddisp;
  DiaObject *obj;
  GPtrArray *textprops;
  TextProperty *prop;

  if ((focus == NULL) || (!focus->has_focus)) return;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  obj = focus_get_object(focus);

  if (obj->ops->get_props == NULL) 
    return;

  make_text_prop_singleton(&textprops,&prop);
  /* Get the first text property */
  obj->ops->get_props(obj, textprops);
  
  /* GTK docs claim the selection clipboard is ignored on Win32.
   * The "clipboard" clipboard is mostly ignored in Unix
   */
#ifdef G_OS_WIN32
  gtk_clipboard_set_text(gtk_clipboard_get(GDK_NONE),
			 prop->text_data, -1);
#else
  gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY),
			 prop->text_data, -1);
#endif
  prop_list_free(textprops);
}

void
edit_cut_text_callback (GtkAction *action)
{
  Focus *focus = active_focus();
  DDisplay *ddisp;
  DiaObject *obj;
  Text *text;
  GPtrArray *textprops;
  TextProperty *prop;
  ObjectChange *change;

  if ((focus == NULL) || (!focus->has_focus)) return;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  obj = focus_get_object(focus);
  text = (Text*)focus->user_data;

  if (obj->ops->get_props == NULL) 
    return;

  make_text_prop_singleton(&textprops,&prop);
  /* Get the first text property */
  obj->ops->get_props(obj, textprops);
  
  /* GTK docs claim the selection clipboard is ignored on Win32.
   * The "clipboard" clipboard is mostly ignored in Unix
   */
#ifdef G_OS_WIN32
  gtk_clipboard_set_text(gtk_clipboard_get(GDK_NONE),
			 prop->text_data, -1);
#else
  gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY),
			 prop->text_data, -1);
#endif

  prop_list_free(textprops);

  if (text_delete_all(text, &change)) { 
    object_add_updates(obj, ddisp->diagram);
    undo_object_change(ddisp->diagram, obj, change);
    undo_set_transactionpoint(ddisp->diagram->undo);
    diagram_modified(ddisp->diagram);
    diagram_flush(ddisp->diagram);
  }
}

void
edit_paste_text_callback (GtkAction *action)
{
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;

#ifdef G_OS_WIN32
  gtk_clipboard_request_text(gtk_clipboard_get(GDK_NONE), 
			     received_clipboard_handler, ddisp);
#else
  gtk_clipboard_request_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), 
			     received_clipboard_handler, ddisp);
#endif
}

void
edit_delete_callback (GtkAction *action)
{
  GList *delete_list;
  DDisplay *ddisp;

  Change *change;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  diagram_selected_break_external(ddisp->diagram);

  delete_list = diagram_get_sorted_selected(ddisp->diagram);
  change = undo_delete_objects_children(ddisp->diagram, delete_list);
  (change->apply)(change, ddisp->diagram);
  g_list_free(delete_list);
  
  diagram_modified(ddisp->diagram);
  diagram_update_extents(ddisp->diagram);

  ddisplay_do_update_menu_sensitivity(ddisp);
  diagram_flush(ddisp->diagram);

  undo_set_transactionpoint(ddisp->diagram->undo);
} 

void
edit_undo_callback (GtkAction *action)
{
  Diagram *dia;
  
  dia = ddisplay_active_diagram();
  if (!dia) return;

  undo_revert_to_last_tp(dia->undo);
  diagram_modified(dia);
  diagram_update_extents(dia);

  diagram_flush(dia);
} 

void
edit_redo_callback (GtkAction *action)
{
  Diagram *dia;
  
  dia = ddisplay_active_diagram();
  if (!dia) return;

  undo_apply_to_next_tp(dia->undo);
  diagram_modified(dia);
  diagram_update_extents(dia);

  diagram_flush(dia);
} 

void
help_manual_callback (GtkAction *action)
{
#ifdef GNOME
  gnome_help_display("dia", NULL, NULL);
#else
  char *helpdir, *helpindex = NULL, *command;
  guint bestscore = G_MAXINT;
  GDir *dp;
  const char *dentry;
  GError *error = NULL;

  helpdir = dia_get_data_directory("help");
  if (!helpdir) {
    message_warning(_("Could not find help directory"));
    return;
  }

  /* search through helpdir for the helpfile that matches the user's locale */
  dp = g_dir_open (helpdir, 0, &error);
  if (!dp) {
    message_warning(_("Could not open help directory:\n%s"),
                    error->message);
    g_error_free (error);
    return;
  }
  
  while ((dentry = g_dir_read_name(dp)) != NULL) {
    guint score;

    score = intl_score_locale(dentry);
    if (score < bestscore) {
      if (helpindex)
	g_free(helpindex);
#ifdef G_OS_WIN32
      /* use HTML Help on win32 if available */
      helpindex = g_strconcat(helpdir, G_DIR_SEPARATOR_S, dentry,
			      G_DIR_SEPARATOR_S "dia-manual.chm", NULL);
      if (!g_file_test(helpindex, G_FILE_TEST_EXISTS)) {
	helpindex = g_strconcat(helpdir, G_DIR_SEPARATOR_S, dentry,
			      G_DIR_SEPARATOR_S "index.html", NULL);
      }
#else
      helpindex = g_strconcat(helpdir, G_DIR_SEPARATOR_S, dentry,
			      G_DIR_SEPARATOR_S "index.html", NULL);
#endif
      bestscore = score;
    }
  }
  g_dir_close (dp);
  g_free(helpdir);
  if (!helpindex) {
    message_warning(_("Could not find help directory"));
    return;
  }

#ifdef G_OS_WIN32
# define SW_SHOWNORMAL 1
  ShellExecuteA (0, "open", helpindex, NULL, helpdir, SW_SHOWNORMAL);
#else
  command = getenv("BROWSER");
  command = g_strdup_printf("%s 'file://%s' &", command ? command : "gnome-open", helpindex);
  system(command);
  g_free(command);
#endif

  g_free(helpindex);
#endif
}

static void 
activate_url (GtkAboutDialog *about,
              const gchar    *link,
	      gpointer        data)
{
#ifdef G_OS_WIN32
  ShellExecuteA (0, "open", link, NULL, NULL, SW_SHOWNORMAL);
#else
  gchar *command = getenv("BROWSER");
  command = g_strdup_printf("%s '%s' &", command ? command : "gnome-open", link);
  system(command);
  g_free(command);
#endif
}

void
help_about_callback (GtkAction *action)
{
  const gchar *translators = _("translator_credits-PLEASE_ADD_YOURSELF_HERE");
  const gchar *license = _(
	"This program is free software; you can redistribute it and/or modify\n"
	"it under the terms of the GNU General Public License as published by\n"
	"the Free Software Foundation; either version 2 of the License, or\n"
	"(at your option) any later version.\n"
	"\n"
	"This program is distributed in the hope that it will be useful,\n"
	"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
	"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
	"GNU General Public License for more details.\n"
	"\n"
	"You should have received a copy of the GNU General Public License\n"
	"along with this program; if not, write to the Free Software\n"
	"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n");

  gchar *dirname = dia_get_data_directory("");
  gchar *filename = g_build_filename (dirname, "dia_logo.png", NULL);
  GdkPixbuf *logo = gdk_pixbuf_new_from_file(filename, NULL);

  gtk_about_dialog_set_url_hook (activate_url, NULL, NULL);
  gtk_show_about_dialog (NULL,
	"logo", logo,
        "name", "Dia",
	"version", VERSION,
	"comments", _("A program for drawing structured diagrams."),
	"copyright", "(C) 1998-2006 The Free Software Foundation and the authors",
	"website", "http://www.gnome.org/projects/dia/",
	"authors", authors,
	"documenters", documentors,
	"translator-credits", strcmp (translators, "translator_credits-PLEASE_ADD_YOURSELF_HERE")
			? translators : NULL,
	"license", license,
	NULL);
  g_free (dirname);
  g_free (filename);
  if (logo)
    g_object_unref (logo);
}

void
view_zoom_in_callback (GtkAction *action)
{
  DDisplay *ddisp;
  Point middle;
  Rectangle *visible;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  visible = &ddisp->visible;
  middle.x = visible->left*0.5 + visible->right*0.5;
  middle.y = visible->top*0.5 + visible->bottom*0.5;
  
  ddisplay_zoom(ddisp, &middle, M_SQRT2);
}

void
view_zoom_out_callback (GtkAction *action)
{
  DDisplay *ddisp;
  Point middle;
  Rectangle *visible;
  
  ddisp = ddisplay_active();
  if (!ddisp) return;
  visible = &ddisp->visible;
  middle.x = visible->left*0.5 + visible->right*0.5;
  middle.y = visible->top*0.5 + visible->bottom*0.5;
  
  ddisplay_zoom(ddisp, &middle, M_SQRT1_2);
}

void
view_zoom_set_callback (GtkAction *action)
{
  DDisplay *ddisp;
  real scale;
  Point middle;
  Rectangle *visible;
  int factor;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  /* HACK the actual factor is a suffix to the action name */
  factor = atoi (gtk_action_get_name (action) + strlen ("ViewZoom"));

  visible = &ddisp->visible;
  middle.x = visible->left*0.5 + visible->right*0.5;
  middle.y = visible->top*0.5 + visible->bottom*0.5;

  scale = ((real) factor)/1000.0 * DDISPLAY_NORMAL_ZOOM;

  ddisplay_zoom(ddisp, &middle, scale / ddisp->zoom_factor);  
}

void
view_show_cx_pts_callback (GtkToggleAction *action)
{
  DDisplay *ddisp;
  int old_val;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  old_val = ddisp->show_cx_pts;
  ddisp->show_cx_pts = gtk_toggle_action_get_active (action);
  
  if (old_val != ddisp->show_cx_pts) {
    ddisplay_add_update_all(ddisp);
    ddisplay_flush(ddisp);
  }
}

void 
view_unfullscreen (void)
{
  DDisplay *ddisp;
  GtkToggleAction *item;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  /* find the menuitem */
  item = GTK_TOGGLE_ACTION (menus_get_action ("ViewFullscreen"));
  if (item && gtk_toggle_action_get_active (item)) {
    gtk_toggle_action_set_active (item, FALSE);
  }
}

void
view_fullscreen_callback (GtkToggleAction *action)
{
  DDisplay *ddisp;
  int fs;

  ddisp = ddisplay_active();
  if (!ddisp) return;

  fs = gtk_toggle_action_get_active (action);
  
  if (fs) /* it is already toggled */
    gtk_window_fullscreen(GTK_WINDOW(ddisp->shell));
  else
    gtk_window_unfullscreen(GTK_WINDOW(ddisp->shell));
}

void
view_aa_callback (GtkToggleAction *action)
{
  DDisplay *ddisp;
  int aa;

  ddisp = ddisplay_active();
  if (!ddisp) return;
 
  aa = gtk_toggle_action_get_active (action);
  
  if (aa != ddisp->aa_renderer) {
    ddisplay_set_renderer(ddisp, aa);
    ddisplay_add_update_all(ddisp);
    ddisplay_flush(ddisp);
  }
}

void
view_visible_grid_callback (GtkToggleAction *action)
{
  DDisplay *ddisp;
  guint old_val;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  
  old_val = ddisp->grid.visible;
  ddisp->grid.visible = gtk_toggle_action_get_active (action); 

  if (old_val != ddisp->grid.visible) {
    ddisplay_add_update_all(ddisp);
    ddisplay_flush(ddisp);
  }
}

void
view_snap_to_grid_callback (GtkToggleAction *action)
{
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  
  ddisplay_set_snap_to_grid(ddisp, gtk_toggle_action_get_active (action));
}

void
view_snap_to_objects_callback (GtkToggleAction *action)
{
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  
  ddisplay_set_snap_to_objects(ddisp, gtk_toggle_action_get_active (action));
}

void 
view_toggle_rulers_callback (GtkToggleAction *action)
{
  DDisplay *ddisp;
  
  ddisp = ddisplay_active();
  if (!ddisp) return;

  if (!gtk_toggle_action_get_active (action)) {
    if (GTK_WIDGET_VISIBLE (ddisp->origin)) {
      gtk_widget_unmap (ddisp->origin);
      gtk_widget_unmap (ddisp->hrule);
      gtk_widget_unmap (ddisp->vrule);
      
      GTK_WIDGET_UNSET_FLAGS (ddisp->origin, GTK_VISIBLE);
      GTK_WIDGET_UNSET_FLAGS (ddisp->hrule, GTK_VISIBLE);
      GTK_WIDGET_UNSET_FLAGS (ddisp->vrule, GTK_VISIBLE);
      
      gtk_widget_queue_resize (GTK_WIDGET (ddisp->origin->parent));
    }
  } else {
    if (!GTK_WIDGET_VISIBLE (ddisp->origin)) {
      GTK_WIDGET_SET_FLAGS (ddisp->origin, GTK_VISIBLE);
      GTK_WIDGET_SET_FLAGS (ddisp->hrule, GTK_VISIBLE);
      GTK_WIDGET_SET_FLAGS (ddisp->vrule, GTK_VISIBLE);
      
      gtk_widget_map (ddisp->origin);
      gtk_widget_map (ddisp->hrule);
      gtk_widget_map (ddisp->vrule);
      
      gtk_widget_queue_resize (GTK_WIDGET (ddisp->origin->parent));
    }
  }
}

extern void
view_new_view_callback (GtkAction *action)
{
  Diagram *dia;

  dia = ddisplay_active_diagram();
  if (!dia) return;
  
  new_display(dia);
}

extern void
view_clone_view_callback (GtkAction *action)
{
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  
  copy_display(ddisp);
}

void
view_show_all_callback (GtkAction *action)
{
  DDisplay *ddisp;
  Diagram *dia;
  real magnify_x, magnify_y;
  int width, height;
  Point middle;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  dia = ddisp->diagram;

  width = dia_renderer_get_width_pixels (ddisp->renderer);
  height = dia_renderer_get_height_pixels (ddisp->renderer);

  /* if there is something selected show that instead of all exisiting objects */
  if (dia->data->selected) {
    GList *list = dia->data->selected;
    Rectangle extents = ((DiaObject*)list->data)->bounding_box;
    list = g_list_next(list);
    while (list) {
      DiaObject *obj = (DiaObject *)list->data;
      rectangle_union(&extents, &(obj->bounding_box));
      list = g_list_next(list);
    }
    magnify_x = (real)width / (extents.right - extents.left) / ddisp->zoom_factor;
    magnify_y = (real)height / (extents.bottom - extents.top) / ddisp->zoom_factor;
    middle.x = extents.left + (extents.right - extents.left) / 2.0;
    middle.y = extents.top + (extents.bottom - extents.top) / 2.0;
  } else {
    magnify_x = (real)width /
      (dia->data->extents.right - dia->data->extents.left) / ddisp->zoom_factor;
    magnify_y = (real)height /
      (dia->data->extents.bottom - dia->data->extents.top) / ddisp->zoom_factor;

    middle.x = dia->data->extents.left +
      (dia->data->extents.right - dia->data->extents.left) / 2.0;
    middle.y = dia->data->extents.top +
      (dia->data->extents.bottom - dia->data->extents.top) / 2.0;
  }

  ddisplay_zoom (ddisp, &middle, 
		 ((magnify_x<magnify_y)?magnify_x:magnify_y)/1.05);

  ddisplay_update_scrollbars(ddisp);
  ddisplay_add_update_all(ddisp);
  ddisplay_flush(ddisp);
}

void
view_redraw_callback (GtkAction *action)
{
  DDisplay *ddisp;
  ddisp = ddisplay_active();
  if (!ddisp) return;
  ddisplay_add_update_all(ddisp);
  ddisplay_flush(ddisp);
}

void
view_diagram_properties_callback (GtkAction *action)
{
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  diagram_properties_show(ddisp->diagram);
}


void
objects_place_over_callback (GtkAction *action)
{
  diagram_place_over_selected(ddisplay_active_diagram());
}

void
objects_place_under_callback (GtkAction *action)
{
  diagram_place_under_selected(ddisplay_active_diagram());
}

void
objects_place_up_callback (GtkAction *action)
{
  diagram_place_up_selected(ddisplay_active_diagram());
}

void
objects_place_down_callback (GtkAction *action)
{
  diagram_place_down_selected(ddisplay_active_diagram());
}

void
objects_parent_callback (GtkAction *action)
{
  diagram_parent_selected(ddisplay_active_diagram());
}

void
objects_unparent_callback (GtkAction *action)
{
  diagram_unparent_selected(ddisplay_active_diagram());
}

void
objects_unparent_children_callback (GtkAction *action)
{
  diagram_unparent_children_selected(ddisplay_active_diagram());
}

void
objects_group_callback (GtkAction *action)
{
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  diagram_group_selected(ddisplay_active_diagram());
  ddisplay_do_update_menu_sensitivity(ddisp);
} 

void
objects_ungroup_callback (GtkAction *action)
{
  DDisplay *ddisp;

  ddisp = ddisplay_active();
  if (!ddisp) return;
  diagram_ungroup_selected(ddisplay_active_diagram());
  ddisplay_do_update_menu_sensitivity(ddisp);
} 

void
dialogs_properties_callback (GtkAction *action)
{
  Diagram *dia;
  DiaObject *selected;

  dia = ddisplay_active_diagram();
  if (!dia) return;

  if (dia->data->selected != NULL) {
    selected = dia->data->selected->data;
    properties_show(dia, selected);
  } else {
    diagram_properties_show(dia);
  } 
}

void
dialogs_layers_callback (GtkAction *action)
{
  layer_dialog_set_diagram(ddisplay_active_diagram());
  layer_dialog_show();
}


void
objects_align_h_callback (GtkAction *action)
{
  const gchar *a;
  int align = DIA_ALIGN_LEFT;
  Diagram *dia;
  GList *objects;

  /* HACK align is suffix to action name */
  a = gtk_action_get_name (action) + strlen ("ObjectsAlign");
  if (0 == strcmp ("Left", a)) {
	align = DIA_ALIGN_LEFT;
  } 
  else if (0 == strcmp ("Center", a)) {
	align = DIA_ALIGN_CENTER;
  } 
  else if (0 == strcmp ("Right", a)) {
	align = DIA_ALIGN_RIGHT;
  } 
  else if (0 == strcmp ("Spreadouthorizontally", a)) {
	align = DIA_ALIGN_EQUAL;
  } 
  else if (0 == strcmp ("Adjacent", a)) {
	align = DIA_ALIGN_ADJACENT;
  }
  else {
	g_warning ("objects_align_v_callback() called without appropriate align");
	return;
  }

  dia = ddisplay_active_diagram();
  if (!dia) return;
  objects = dia->data->selected;
  
  object_add_updates_list(objects, dia);
  object_list_align_h(objects, dia, align);
  diagram_update_connections_selection(dia);
  object_add_updates_list(objects, dia);
  diagram_modified(dia);
  diagram_flush(dia);     

  undo_set_transactionpoint(dia->undo);
}

void
objects_align_v_callback (GtkAction *action)
{
  const gchar *a;
  int align;
  Diagram *dia;
  GList *objects;

  /* HACK align is suffix to action name */
  a = gtk_action_get_name (action) + strlen ("ObjectsAlign");
  if (0 == strcmp ("Top", a)) {
	align = DIA_ALIGN_TOP;
  } 
  else if (0 == strcmp ("Middle", a)) {
	align = DIA_ALIGN_CENTER;
  } 
  else if (0 == strcmp ("Bottom", a)) {
	align = DIA_ALIGN_BOTTOM;
  } 
  else if (0 == strcmp ("Spreadoutvertically", a)) {
	align = DIA_ALIGN_EQUAL;
  } 
  else if (0 == strcmp ("Stacked", a)) {
	align = DIA_ALIGN_ADJACENT;
  }
  else {
	g_warning ("objects_align_v_callback() called without appropriate align");
	return;
  }

  dia = ddisplay_active_diagram();
  if (!dia) return;
  objects = dia->data->selected;

  object_add_updates_list(objects, dia);
  object_list_align_v(objects, dia, align);
  diagram_update_connections_selection(dia);
  object_add_updates_list(objects, dia);
  diagram_modified(dia);
  diagram_flush(dia);     

  undo_set_transactionpoint(dia->undo);
}

