/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * filename: xviewer.c                                                     *
 *                                                                         *
 * UTIL C-source: Medical Image Conversion Utility                         *
 *                                                                         *
 * purpose      : image viewer routines                                    *
 *                                                                         *
 * project      : (X)MedCon by Erik Nolf                                   *
 *                                                                         *
 * Functions    : XMdcGetBoardDimensions()     - Get checker board display *
 *                XMdcHandleBoardDimensions()  - Create image number arrays*
 *                XMdcBuildViewerWindow()      - Build viewer window       *
 *                XMdcViewerHide()             - Hide the viewer           *
 *                XMdcViewerShow()             - Show the viewer           *
 *                XMdcViewerEnableAutoShrink() - Enable auto shrinking     *
 *                XMdcViewerDisableAutoShrink()- Disable auto shrinking    *
 *                                                                         *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 */

/*
   Copyright (C) 1997-2026 by Erik Nolf

   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, 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

/****************************************************************************
                              H E A D E R S
****************************************************************************/

#include "m-depend.h"

#include <stdio.h>
#include <math.h>
#ifdef LIBMDC_HAVE_STDLIB_H
#include <stdlib.h>
#endif

#include "xmedcon.h"

/****************************************************************************
                            F U N C T I O N S
****************************************************************************/

int XMdcGetBoardDimensions(void)
 {
   Uint32 i, total_images, real_images, mwidth, mheight;
   Uint32 arbitrary_row, arbitrary_col;
   Uint32 arbitrary_images_per_page, arbitrary_pages;
   Uint32 trial_images_vertical = 0, trial_images_horizontal = 0;
   int    FOUND = MDC_NO;

   real_images = XMdcPagesGetNrImages();

   if (real_images == 0)
     XMdcDisplayFatalErr(MDC_BAD_CODE,"Number of images is zero !?");

   mwidth  = XMdcScaleW(my.fi->mwidth);
   mheight = XMdcScaleH(my.fi->mheight);

   /* TRIAL#1: original number of images is a neat square ? */
   total_images = real_images;
   if ((total_images % (Uint32)sqrt((double)total_images)) == 0 ) {

     FOUND = MDC_YES;

     trial_images_vertical   = (Uint32)sqrt((double)total_images);
     trial_images_horizontal = total_images / trial_images_vertical;

     /* inside the screen boundaries ? */
     if ((trial_images_vertical   * mheight) >
         (my.screen_height - XMDC_FREE_BORDER)) FOUND = MDC_NO;
     if ((trial_images_horizontal * mwidth) >
         (my.screen_width  - XMDC_FREE_BORDER)) FOUND = MDC_NO;

   }

   if (FOUND == MDC_NO) {
     /* TRIAL#2: based on divisors of closest even number */
     total_images = real_images;
     if (total_images % 2) total_images += 1;
     for (i=(Uint32)sqrt((double)total_images); i>=1; i--) {

        if (!(total_images % i)) {

          FOUND = MDC_YES;

          /* found a possible dimension */
          trial_images_vertical   = i;
          trial_images_horizontal = total_images / trial_images_vertical;

          /* inside the screen boundaries ? */
          if ((trial_images_vertical   * mheight) >
              (my.screen_height - XMDC_FREE_BORDER)) FOUND = MDC_NO;
          if ((trial_images_horizontal * mwidth) >
              (my.screen_width  - XMDC_FREE_BORDER)) FOUND = MDC_NO;

        }

        if (FOUND == MDC_YES) break;

     }
   }

   if (FOUND == MDC_NO) {
     /* TRIAL#3: based on closest square number */

     /* always ok, except for screen boundaries */
     FOUND = MDC_YES;
     total_images = real_images;
     while ( total_images % (Uint32)sqrt((double)total_images) ) {
          total_images += 1;
     }
     trial_images_vertical   = (Uint32)sqrt((double)total_images);
     trial_images_horizontal = total_images / trial_images_vertical;

     /* inside the screen boundaries ? */
     if ((trial_images_vertical   * mheight) >
         (my.screen_height - XMDC_FREE_BORDER)) FOUND = MDC_NO;
     if ((trial_images_horizontal * mwidth) >
         (my.screen_width  - XMDC_FREE_BORDER)) FOUND = MDC_NO;

   }

   if (FOUND == MDC_YES) {
     my.images_vertical   = trial_images_vertical;
     my.images_horizontal = trial_images_horizontal;

     /* now get pages & images per page    */
     my.images_per_page = my.images_vertical * my.images_horizontal;
     if (real_images < my.images_per_page) {
       my.number_of_pages = (my.fi->number + real_images - 1)
                              / real_images;
     }else{
       my.number_of_pages = (my.fi->number + my.images_per_page - 1)
                              / my.images_per_page;
     }
     if ((real_images < my.images_per_page) && (my.images_vertical == 1)) {
       /* Ola, all images fit on one row ! (ex.: 1 image) -> No board */
       my.images_horizontal = real_images;
       my.images_per_page = my.images_vertical * my.images_horizontal;
       my.number_of_pages = (my.fi->number + my.images_per_page - 1)
                              / my.images_per_page;
     }

     return(MDC_OK);

   }

   /* final fallback based on screen dimensions */
   arbitrary_row = (my.screen_height - XMDC_FREE_BORDER)
                    / XMdcScaleH(my.fi->mheight);
   arbitrary_col = (my.screen_width  - XMDC_FREE_BORDER)
                    / XMdcScaleW(my.fi->mwidth);

   if (arbitrary_row == 0 || arbitrary_col == 0) {
     arbitrary_row = 1; arbitrary_col = 1;
   }

   arbitrary_images_per_page = arbitrary_row * arbitrary_col;
   arbitrary_pages = (my.fi->number+(arbitrary_images_per_page - 1))
                         / arbitrary_images_per_page;

   my.images_vertical   = arbitrary_row;
   my.images_horizontal = arbitrary_col;
   my.images_per_page   = arbitrary_images_per_page;
   my.number_of_pages   = arbitrary_pages;

   /* we still have to take care of less images per page */
   if (XMdcPagesGetNrImages() < my.images_per_page ) {
     my.images_per_page = XMdcPagesGetNrImages();
     my.number_of_pages  = (my.fi->number+(my.images_per_page - 1))
                           / my.images_per_page;

     my.images_vertical  = (my.images_per_page + (my.images_horizontal - 1))
                           / my.images_horizontal;
   }

   if ((my.fi->number < my.images_per_page) && (my.images_vertical == 1)) {
     /* Ola, all images fit on one row ! (ex.: 1 image) -> No chessboard */
     my.images_horizontal = my.fi->number;
     my.images_per_page   = my.images_vertical * my.images_horizontal;
     my.number_of_pages   = 1;

   }else if (my.fi->number < my.images_per_page) {
     /* Ola, there could have been to much rows provided */
     for (i=1; i<=my.images_vertical; i++) {
        if (i*my.images_horizontal >= my.fi->number) {
          my.images_vertical = i;
          my.images_per_page = my.images_vertical * my.images_horizontal;
          my.number_of_pages = 1;
        }
     }
   }

   return(MDC_OK);

}

void XMdcHandleBoardDimensions(void)
{
  Uint32 vertical, i;
  Uint32 h=my.fi->mheight;

  /* derive colormap dimensions */
  vertical = my.images_vertical;

  if (vertical*(XMdcScaleH(h)+(XMDC_IMAGE_BORDER<<1)) < XMDC_COLORMAP_HEIGHT) {
    my.cmap_h = XMDC_COLORMAP_HEIGHT;
  }else{
    my.cmap_h = vertical * (XMdcScaleH(h) + XMDC_IMAGE_BORDER);
  }
  my.cmap_w = XMDC_COLORMAP_WIDTH;

  my.im=(GdkPixbuf **)malloc(sizeof(GdkPixbuf *) * my.images_per_page);
  if (my.im == NULL)  {
    MdcCleanUpFI(my.fi);
    XMdcDisplayFatalErr(MDC_BAD_ALLOC,"Couldn't malloc GdkPixbuf array");
  }
  my.imagenumber=(Uint32 *)malloc(sizeof(Uint32)*my.images_per_page);
  if (my.imagenumber == NULL) {
    MdcFree(my.im);
    MdcCleanUpFI(my.fi);
    XMdcDisplayFatalErr(MDC_BAD_ALLOC,"Couldn't malloc ImageNumbers array");
  }
  my.realnumber =(Uint32 *)malloc(sizeof(Uint32)*my.images_per_page);
  if (my.realnumber == NULL) {
    MdcFree(my.im);
    MdcFree(my.imagenumber);
    MdcCleanUpFI(my.fi);
    XMdcDisplayFatalErr(MDC_BAD_ALLOC,"Couldn't malloc RealNumbers array");
  }
  my.pagenumber=(Uint32 *)malloc(sizeof(Uint32)*my.number_of_pages);
  if (my.pagenumber == NULL) {
    MdcFree(my.im);
    MdcFree(my.imagenumber);
    MdcFree(my.realnumber);
    MdcCleanUpFI(my.fi);
    XMdcDisplayFatalErr(MDC_BAD_ALLOC,"Couldn't malloc PageNumbers array");
  }
  my.image=(GtkWidget **)malloc(sizeof(GtkWidget *)*my.images_per_page);
  if (my.image == NULL) {
    MdcFree(my.im);
    MdcFree(my.imagenumber);
    MdcFree(my.realnumber);
    MdcFree(my.pagenumber);
    MdcCleanUpFI(my.fi);
    XMdcDisplayFatalErr(MDC_BAD_ALLOC,"Couldn't malloc Images array");
  }

  for (i = 0; i<my.images_per_page; i++) {
     my.im[i] = NULL;
     my.imagenumber[i]=i;
     my.realnumber[i]=0;
     my.image[i]=NULL;
  }

  for (i = 0; i<my.number_of_pages; i++) my.pagenumber[i] = i;

  MdcDebugPrint("screen_width      = %d",my.screen_width);
  MdcDebugPrint("screen_height     = %d",my.screen_height);
  MdcDebugPrint("mwidth            = %u",my.fi->mwidth);
  MdcDebugPrint("mheight           = %u",my.fi->mheight);
  MdcDebugPrint("resize            = %d",sResizeSelection.CurType);
  MdcDebugPrint("fi.number         = %u",my.fi->number);
  MdcDebugPrint("fi.dim[3]         = %u",my.fi->dim[3]);
  MdcDebugPrint("curpage           = %u",my.curpage);
  MdcDebugPrint("images_horizontal = %u",my.images_horizontal);
  MdcDebugPrint("images_vertical   = %u",my.images_vertical);
  MdcDebugPrint("images_per_page   = %u",my.images_per_page);
  MdcDebugPrint("number_of_pages   = %u",my.number_of_pages);
  MdcDebugPrint("cmap_w            = %u",my.cmap_w);
  MdcDebugPrint("cmap_h            = %u",my.cmap_h);

}

void XMdcBuildViewerWindow(void)
{
  GtkWidget *box1;
  GtkWidget *box2;
  GtkWidget *vbox;
  GtkWidget *label;
  GtkWidget *frame;
  GtkWidget *winbox;
  GtkWidget *button;
  GtkWidget *spinner;
  GtkWidget *entrybox;
  GtkWidget *separator;
  GtkAdjustment *adj;

  if (my.viewwindow == NULL) {
    my.viewwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_resizable(GTK_WINDOW(my.viewwindow), FALSE);

    g_signal_connect(my.viewwindow, "delete-event",
                       G_CALLBACK(XMdcViewerHide), NULL);
    g_signal_connect(my.viewwindow, "destroy",
                       G_CALLBACK(XMdcMedconQuit), NULL);
    gtk_container_set_border_width(GTK_CONTAINER (my.viewwindow), 0);
  }

  gtk_window_set_title(GTK_WINDOW(my.viewwindow), my.fi->ifname);

  gtk_widget_realize(my.viewwindow);

  box1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  gtk_container_add(GTK_CONTAINER (my.viewwindow), box1);
  gtk_widget_show(box1);

  my.viewbox = box1;

  box2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_pack_start(GTK_BOX(box1),box2,FALSE,FALSE,0);
  gtk_widget_show(box2);

  my.pagemenu = XMdcPagesCreateMenu();

  gtk_box_pack_start(GTK_BOX(box2),my.pagemenu,TRUE,TRUE,0);
  gtk_widget_show(my.pagemenu);

  button = gtk_button_new_with_label("Next");
  g_signal_connect(button, "button-release-event",
                     G_CALLBACK(XMdcPagesCbNext),NULL);
  gtk_box_pack_start(GTK_BOX(box2),button,TRUE,TRUE,0);
  gtk_widget_show(button);

  button = gtk_button_new_with_label("Prev");
  g_signal_connect(button, "button-release-event",
                     G_CALLBACK(XMdcPagesCbPrev),NULL);
  gtk_box_pack_start(GTK_BOX(box2),button,TRUE,TRUE,0);
  gtk_widget_show(button);

  /* layout for images and colormap */
  frame = gtk_frame_new(NULL);
  gtk_container_set_border_width(GTK_CONTAINER(frame),4);
  gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
  gtk_box_pack_start(GTK_BOX(box1),frame,TRUE,TRUE,0);
  gtk_widget_show(frame);

  winbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_container_add(GTK_CONTAINER(frame),winbox);
  gtk_widget_show(winbox);

  my.imgsbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0);
  gtk_container_set_border_width(GTK_CONTAINER(my.imgsbox),0);
  gtk_box_pack_start(GTK_BOX(winbox),my.imgsbox,TRUE,TRUE,0);
  gtk_widget_show(my.imgsbox);

  my.imgsgrid=gtk_grid_new();
  gtk_box_pack_start(GTK_BOX(my.imgsbox), my.imgsgrid, TRUE, TRUE, 0);
  gtk_widget_show(my.imgsgrid);

  separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
  gtk_box_pack_start(GTK_BOX(winbox), separator, FALSE, FALSE, 0);
  gtk_widget_show(separator);

  my.cmapbox = gtk_event_box_new();
  gtk_box_pack_start(GTK_BOX(winbox),my.cmapbox,FALSE,FALSE,0);
  gtk_widget_show(my.cmapbox);

  XMdcApplyMapPlace(XMDC_CMAP_PLACE);

  /* seperator */
  separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
  gtk_box_pack_start(GTK_BOX(box1), separator, FALSE, TRUE, 0);

  /* layout for buttons */

  vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
  gtk_container_set_border_width(GTK_CONTAINER(box2), 0);
  gtk_box_pack_start(GTK_BOX(box1), vbox, FALSE, TRUE, 0);
  gtk_widget_show(vbox);

  box2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
  gtk_container_set_border_width(GTK_CONTAINER(box2), 0);
  gtk_box_pack_start(GTK_BOX(vbox), box2, FALSE, FALSE, 0);
  gtk_widget_show(box2);


  button = gtk_button_new_with_label("Toggle Entries");
  gtk_box_pack_start(GTK_BOX(box2),button,TRUE,TRUE,0);
  gtk_widget_show(button);

  entrybox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
  gtk_container_set_border_width(GTK_CONTAINER(box2), 0);
  gtk_box_pack_start(GTK_BOX(box2), entrybox, FALSE, FALSE, 0);
  gtk_widget_show(entrybox);

  g_signal_connect_swapped(button, "clicked",
                            G_CALLBACK(XMdcToggleVisibility),
                            entrybox);

  label = gtk_label_new("Page: ");
  gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_RIGHT);
  gtk_widget_set_name(label,"FixedLabel");
  gtk_box_pack_start(GTK_BOX(entrybox),label,FALSE,TRUE,5);
  gtk_widget_show(label);

  adj=(GtkAdjustment *)gtk_adjustment_new(1., 1., (float)my.number_of_pages,
                                          1., 5., 0.);

  spinner = gtk_spin_button_new(adj, 0.0, 0);
  gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinner), TRUE);
  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spinner), TRUE);
  gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON(spinner), TRUE);

  gtk_box_pack_start(GTK_BOX(entrybox),spinner,FALSE,FALSE,5);

  g_signal_connect(spinner, "value_changed",
                     G_CALLBACK(XMdcPagesCbGoTo),NULL);
  gtk_widget_show(spinner);

  label = gtk_label_new("Table: ");
  gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_RIGHT);
  gtk_widget_set_name(label,"FixedLabel");
  gtk_box_pack_start(GTK_BOX(entrybox),label,FALSE,TRUE,5);
  gtk_widget_show(label);

  if (XMEDCONLUT != NULL) {
    /* preference for external files */
    adj=(GtkAdjustment *)gtk_adjustment_new(1., 1., (float)XMDC_MAX_LOADABLE_LUTS,
                                            1., 5., 0.);
  }else{
    /* use internal palette */
    adj=(GtkAdjustment *)gtk_adjustment_new(1., 1., (float)(mdc_max_palette-1),
                                            1., 5., 0.);
  }

  spinner = gtk_spin_button_new(adj, 0.0, 0);
  gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinner), TRUE);
  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spinner), TRUE);
  gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON(spinner), TRUE);

  gtk_box_pack_start(GTK_BOX(entrybox),spinner,FALSE,FALSE,5);

  g_signal_connect(spinner, "value_changed",
                     G_CALLBACK(XMdcChangeLUT),NULL);
  gtk_widget_show(spinner);

  box2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
  gtk_container_set_border_width(GTK_CONTAINER(box2), 0);
  gtk_box_pack_start(GTK_BOX(vbox), box2, FALSE, TRUE, 0);
  gtk_widget_show(box2);

  button = gtk_button_new_with_label(" Hide ");
  g_signal_connect(button, "clicked",
                     G_CALLBACK(XMdcViewerHide), NULL);
  gtk_box_pack_start(GTK_BOX(box2), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  button = gtk_button_new_with_label("Labels");
  g_signal_connect(button, "clicked",
                     G_CALLBACK(XMdcLabelSel), NULL);
  gtk_box_pack_start(GTK_BOX(box2), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  button = gtk_button_new_with_label("Render");
  g_signal_connect(button, "clicked",
                     G_CALLBACK(XMdcRenderingSel), NULL);
  gtk_box_pack_start(GTK_BOX(box2), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

}

void XMdcViewerHide(void)
{
  if (my.viewwindow != NULL) {
    gtk_widget_hide(my.viewwindow);
  }
}

void XMdcViewerShow(void)
{
  if (XMDC_FILE_OPEN == MDC_NO) return;
  if (my.viewwindow != NULL) {
    MdcDebugPrint("Show viewer window ...");
    gtk_widget_show(my.viewwindow);
  }
  XMdcViewerDisableAutoShrink();
}

void XMdcViewerEnableAutoShrink(void)
{
  if (my.viewwindow != NULL) {
    MdcDebugPrint("Enable auto shrink viewer window");
    gtk_window_set_resizable(GTK_WINDOW(my.viewwindow), TRUE);
  }
}

void XMdcViewerDisableAutoShrink(void)
{
  if (my.viewwindow != NULL) {
    MdcDebugPrint("Disable auto shrink viewer window");
    gtk_window_set_resizable(GTK_WINDOW(my.viewwindow), FALSE);
  }
}

