Logo Search packages:      
Sourcecode: xconq version File versions  Download package

maclist.c

/* Unit lists for the Mac interface to Xconq.
   Copyright (C) 1992-1996, 1998, 2000 Stanley T. Shebs.

Xconq 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.  See the file COPYING.  */

#include "conq.h"
#include "macconq.h"

static void init_list_contents(List *list);
static void organize_list_contents(List *list);
static void sort_list_contents(List *list);
static void add_unit_to_list(List *list, Unit *unit);

static void set_list_scrollbars(List *list);
static void draw_list_contents(List *list);
static void draw_list_headings(List *list);
static void draw_unit_list_entry(List *list, int n, int clearfirst);
static void adjust_list_decor(List *list);

static pascal void list_vscroll_fn(ControlHandle control, short code);
static pascal void list_hscroll_fn(ControlHandle control, short code);
static void auto_scroll_list_up_arrow();
static void auto_scroll_list_down_arrow();
static void auto_scroll_list_right_arrow();
static void auto_scroll_list_left_arrow();

static void insert_unit_into_list(List *list, Unit *unit);
static void remove_unit_from_list(List *list, Unit *unit);
static int unit_position_in_list(List *list, Unit *unit);
static void redraw_unit_list_entry(List *list, int n);
static void clear_selections(List *list);


/* (should be adjusted for expected range of values) */

int curactcolw = 10;
int imagecolw = 20;
int namecolw = 100;
int typecolw = 80;
int sidecolw = 70;
int hpcolw = 40;
int acpcolw = 50;
int poscolw = 50;
int taskcolw = 130;
int supcolw = 40;
int notecolw = 50;

/* A notes column is needed only if units are scheduled to appear. */

int notes_column_needed = -1;

/* A supply column is needed only if any unit can store the mtype. */

int *sup_column_needed = NULL;

/* Sum of non-scrolling field widths. */

int fixedfieldwidth;

/* Sum of all field widths. */

int maxlistwidth;

/* The height of the headings lines at the top of the window. */

int listtoph = 20;

int listtopbaseline = 13;

/* The possible heights of each individual list entry. */

int smallentryspacing = 24;
int largeentryspacing = 46;

int listnum = 1;

int lastlisth = -1, lastlistv = -1;

ControlActionUPP list_vscroll_proc;
ControlActionUPP list_hscroll_proc;

/* Create a new list of units. */

void
create_list()
{
      int i, m;
      List *list = (List *) xmalloc(sizeof(List));
      Unit *unit;
      Rect hscrollrect, vscrollrect;

      /* If this is the first list, test if we need supply columns. */ 
      if (sup_column_needed == NULL) {
            sup_column_needed = (int *) xmalloc(sizeof(int) * nummtypes);
            for_all_material_types(m) {
                  sup_column_needed[m] = storage_possible(m);
            }           
      }
      /* If this is the first list, test if we need a notes column. */ 
      if (notes_column_needed == -1) {
            for_all_units(unit) {
                  if (side_sees_unit(dside, unit)
                        && !inside_area(unit->x, unit->y)
                        && unit_appear_turn(unit) >= 0) {
                        notes_column_needed = TRUE;
                        break;
                  }
                  notes_column_needed = FALSE;
            }
      }
      maxlistwidth = 0;
      maxlistwidth += curactcolw;
      maxlistwidth += imagecolw;
      maxlistwidth += namecolw;
      fixedfieldwidth = maxlistwidth;
      maxlistwidth += typecolw;
      maxlistwidth += sidecolw;
      maxlistwidth += hpcolw;
      maxlistwidth += acpcolw;
      maxlistwidth += poscolw;
      for_all_material_types(m) {
            if (sup_column_needed[m])
                maxlistwidth += supcolw;
      }
      maxlistwidth += taskcolw;
      if (notes_column_needed) {
            maxlistwidth += notecolw;
      }
      DGprintf("Creating a list\n");
      /* Default to listing every side and indeps too. */
      list->sides = ALLSIDES;
      list->completed_units = TRUE;
      list->incomplete_units = TRUE;
      for (i = 0; i < MAXSORTKEYS; ++i)
        list->sortkeys[i] = bynothing;
      list->sortkeys[0] = byside; /* (should be a preference) */
      list->mainsortmi = miViewBySide;
      list->largeicons = FALSE; /* (should be a preference) */
      list->sidecolors = default_sidecolors;    /* same default as for maps */
      list->drawsizes = default_drawsizes;      /* same default as for maps */
      list->draw_emblems = default_draw_emblems;      /* same default as for maps */
      /* (should share with toggle routine?) */
      list->entryspacing = (list->largeicons ? largeentryspacing : smallentryspacing);
      list->firstvisible = 0;
      init_list_contents(list);
      organize_list_contents(list);
      list->next = listlist;
      listlist = list;
      /* Make a window for the list, give it scrollbars. */
      if (hasColorQD)
            list->window = GetNewCWindow(wList, nil, NULL);
      else  list->window = GetNewWindow(wList, nil, NULL);
      stagger_window(list->window, &lastlisth, &lastlistv);
      SelectTheWindow(list->window);
      SetPort(list->window);
      TextFont(small_font_id);
      TextSize(small_font_size);
      /* Create the scrollbars. */
      hscrollrect = list->window->portRect;
      hscrollrect.top = hscrollrect.bottom - sbarwid;
      hscrollrect.bottom += 1;
      hscrollrect.left = fixedfieldwidth - 5;
      hscrollrect.right -= sbarwid - 1;
      list->hscrollbar = NewControl(list->window, &hscrollrect, "\p", TRUE,
                                                  0, 0, 100, scrollBarProc, 0L);
      vscrollrect = list->window->portRect;
      vscrollrect.top = listtoph - 1;
      vscrollrect.bottom -= sbarwid - 1;
      vscrollrect.left = vscrollrect.right - sbarwid;
      vscrollrect.right += 1;
      list->vscrollbar = NewControl(list->window, &vscrollrect, "\p", TRUE,
                                                  0, 0, 100, scrollBarProc, 0L);
      /* Now set the scrollbars to their *real* limits. */
      set_list_scrollbars(list);
      /* Needed for list number cleanup. */
      list->id = listnum++;
      sprintf(spbuf, "List %d", list->id);
      add_window_menu_item(spbuf, list->window);
}

/* Make the list be empty. */

void
init_list_contents(List *list)
{
      /* Set up a unit vector with a first cut at needed space;
         will adjust upwards automatically if necessary. */
      list->contents = make_unit_vector(numunits + 50);
      list->numunits = 0;
}

/* This takes the list and fills in the items it is to display. */

void
organize_list_contents(List *list)
{
      int numvisunits, n, first = FALSE;
      Side *side2;
      Unit *unit, *unit2 = NULL, *unit3 = NULL;
      Rect winrect = list->window->portRect;

      /* Find and save the first visible non-dead unit in the list. */
      n = list->firstvisible;
      while (list->numunits > 0 && !first) {
            unit2 = unit_in_vector(list->contents, n);
            if (in_play(unit2))
                first = TRUE;
            n++;
      }
      /* Save the selected unit. */
      unit3 = selected_unit_in_list(list);      

      /* Build up the array of units for this list. */
      list->numunits = 0;
      clear_unit_vector(list->contents);
      /* We always see our own units. */
      for_all_side_units(dside, unit) {
            add_unit_to_list(list, unit);
      }
      for_all_sides(side2) {
            if (dside != side2) {
                  for_all_side_units(side2, unit) {
                        if (side_sees_image(dside, unit)) {
                              add_unit_to_list(list, unit);
                        }
                  }
            }
      }
      /* Now sort the list according to its keys. */
      sort_list_contents(list);
      
      /* Restore the old selected unit. */
      if (in_play(unit3)
          && (n = unit_position_in_list(list, unit3)) >= 0) {     
                list->contents->units[n].flag = TRUE;
      }
      /* Restore the old first visible unit if possible. */
      if (first) {
            list->firstvisible = unit_position_in_list(list, unit2);
            numvisunits = (winrect.bottom - winrect.top - listtoph - sbarwid) / list->entryspacing;
            numvisunits = min(numvisunits, list->numunits);
            list->lastvisible = list->firstvisible + numvisunits - 1;
            /* Check that lastvisible is less than numunits. */
            list->lastvisible = min(list->lastvisible, list->numunits - 1);
            /* Readjust firstvisible if necessary. */
            list->firstvisible = max(0, min(list->firstvisible, list->lastvisible - numvisunits + 1));
      }
      /* The number of units may have changed, so we need to update the scrollbars. */
      set_list_scrollbars(list);
}

void
sort_list_contents(List *list)
{
      int i;

      for (i = 0; i < MAXSORTKEYS; ++i) {
            tmpsortkeys[i] = list->sortkeys[i];
      }
      sort_unit_vector(list->contents);
}

void
add_unit_to_list(List *list, Unit *unit)
{
      if (alive(unit)
          && (completed(unit) ? list->completed_units : list->incomplete_units)) {
            list->contents = add_unit_to_vector(list->contents, unit, FALSE);
            /* (should apply other inclusion criteria too?) */
            ++list->numunits;
      }
}

/* Calculate reasonable/valid values and maxima for the scrollbars,
   starting from the window size. */

void
set_list_scrollbars(List *list)
{
      int numvisunits, visfieldwidth, val, maxval;
      Rect winrect = list->window->portRect;

      /* Set the vertical scrollbar correctly. */     
      numvisunits = (winrect.bottom - winrect.top - sbarwid - listtoph) / list->entryspacing;
      maxval = max(0, list->numunits - numvisunits);
      SetCtlMax(list->vscrollbar, maxval);
      SetCtlValue(list->vscrollbar, min(list->firstvisible, maxval));
      /* Set up the horizontal scrollbar. */
      visfieldwidth = winrect.right - winrect.left - sbarwid - fixedfieldwidth;
      maxval = (maxlistwidth - fixedfieldwidth) - visfieldwidth;
      maxval = max(0, maxval);
      SetCtlMax(list->hscrollbar, maxval);
      SetCtlValue(list->hscrollbar, min(list->firstvisfield, maxval));
}

List *
list_from_window(WindowPtr window)
{
      List *list;
      
      if (dside == NULL) return NULL;
      for_all_lists(list) {
            if (list->window == window) return list;
      }
      return NULL;
}

void
draw_list(List *list)
{
      WindowPtr listwin = list->window;
      Rect tmprect;
      RgnHandle tmprgn;

      /* Erase the content tect. */
      tmprect = listwin->portRect;
      tmprect.right -= sbarwid;
      tmprect.bottom -= sbarwid;
      BackPat(QDPat(white));
      EraseRect(&tmprect);
      /* Also erase small rect to the left of the bottom
      scrollbar. */
      tmprect = listwin->portRect;
      tmprect.right = tmprect.left + fixedfieldwidth - 5;
      tmprect.top = tmprect.bottom - sbarwid;
      BackPat(QDPat(white));
      EraseRect(&tmprect);
      /* Make sure the list is up to date before redrawing it. */
      organize_list_contents(list);
      tmprgn = NewRgn();
      GetClip(tmprgn);
      draw_list_contents(list);
      SetClip(tmprgn);
      DisposeRgn(tmprgn);
}

void
draw_list_contents(List *list)
{
      int line, numvisunits, viswidth;
      Rect winrect = list->window->portRect;

      /* Image is basically square, but add a bit of extra space on each side. */
      imagecolw = list->entryspacing + 2;
      /* Draw the selection and sorting as a sort of header. */
      draw_list_headings(list);
      /* Compute how many list elements are actually visible. */
      numvisunits = (winrect.bottom - winrect.top - listtoph - sbarwid) / list->entryspacing;
      numvisunits = min(numvisunits, list->numunits);
      list->lastvisible = list->firstvisible + numvisunits - 1;
      /* Check that lastvisible is less than numunits. */
      list->lastvisible = min(list->lastvisible, list->numunits - 1);
      /* Readjust firstvisible if necessary. */
      list->firstvisible = max(0, min(list->firstvisible, list->lastvisible - numvisunits + 1));
      for (line = list->firstvisible; line <= list->lastvisible; ++line) {
            draw_unit_list_entry(list, line, FALSE);
      }
      viswidth = winrect.right - winrect.left - sbarwid;
}

void
draw_list_headings(List *list)
{
      int x = 0, m;
      Str255 tmpstr;
      Rect cliprect, tmprect, winrect = list->window->portRect;
      RGBColor tmpcolor;

      cliprect = winrect;
      cliprect.right -= sbarwid;
      ClipRect(&cliprect);
      /* Clear the heading area. */
      SetRect(&tmprect, 0, 0, winrect.right - sbarwid, listtoph);
      EraseRect(&tmprect);
      /* Draw dividing lines that cross both fixed and scrolling fields. */
      MoveTo(0, listtoph - 3);
      Line(winrect.right, 0);
      MoveTo(0, listtoph - 1);
      Line(winrect.right, 0);
      /* Draw a dividing line between the fixed and mobile fields. */
      MoveTo(fixedfieldwidth - 5, 0);
      LineTo(fixedfieldwidth - 5, listtoph - 1);
      /* (should underline sort keys with varying line heaviness) */
      /* We have to do MoveTo everywhere because DrawString moves the pen. */
      /* First draw headings for fields not affected by horizontal scrolling. */
      x += curactcolw;
      MoveTo(x, listtopbaseline);
      DrawString("\ps/L");
      x += imagecolw;
      MoveTo(x, listtopbaseline);
      DrawString("\pName/Number");
      /* Shift left by horiz scroll. */
      x -= list->firstvisfield;
      /* Now clip against the fixed fields' area. */
      cliprect = winrect;
      cliprect.right -= sbarwid;
      cliprect.left = fixedfieldwidth;
      ClipRect(&cliprect);
      x += namecolw;
      MoveTo(x, listtopbaseline);
      DrawString("\pType");
      x += typecolw;
      MoveTo(x, listtopbaseline);
      DrawString("\pSide");
      x += sidecolw;
      MoveTo(x, listtopbaseline);
      DrawString("\pHp");
      x += hpcolw;
      MoveTo(x, listtopbaseline);
      DrawString("\pAcp");
      MoveTo(x + 20, listtopbaseline);
      tmpcolor = (RGBColor) {0x8FFF, 0x8FFF, 0x8FFF};
      RGBForeColor(&tmpcolor);
      DrawString("\pCp");
      ForeColor(blackColor);
      x += acpcolw;
      MoveTo(x, listtopbaseline);
      DrawString("\pLocation");
      x += poscolw;
      MoveTo(x, listtopbaseline);
      DrawString("\pTask");
      x += taskcolw;
      for_all_material_types(m) {
            if (sup_column_needed[m]) {
                  MoveTo(x, listtopbaseline);
                  c2p(m_type_name(m), tmpstr);
                  DrawString(tmpstr);
                  x += supcolw;
            }
      }
      if (notes_column_needed) {
            MoveTo(x, listtopbaseline);
            DrawString("\pNotes");
            x += notecolw;
      }
      /* Draw a gray line indicating the end of the data columns. */
      PenPat(QDPat(gray));
      MoveTo(x, 0);
      Line(0, listtoph - 4);
      PenNormal();
}

/* This draws a one-line entry for the given unit. */

/* This routine does *not* save/restore the clip region, but does
   modify it, so it should not be used except in a safe context. */

void
draw_unit_list_entry(List *list, int n, int clearfirst)
{
      Unit *unit = unit_in_vector(list->contents, n);
      Rect bbox = (*(list->window->visRgn))->rgnBBox;
      Rect winrect = list->window->portRect;
      Rect entryrect, tmprect, cliprect;
      char tmpnbuf[BUFSIZE];
      int u, m, x, y, texty;
      RGBColor tmpcolor;
      Side *side2;
      
      y = (n - list->firstvisible) * list->entryspacing + listtoph;
      texty = y + 15;
      /* Clip to the entry rect. */
      SetRect(&entryrect, 0, y, winrect.right - sbarwid, y + list->entryspacing);
      cliprect = entryrect;
      ClipRect(&cliprect);
      /* Draw incomplete unit on a blue background. */
      if (!completed(unit)) {
            tmpcolor = (RGBColor) {0xCFFF, 0xFFFF, 0xFFFF};
            RGBForeColor(&tmpcolor);
            FillRect(&entryrect, QDPat(black));
            ForeColor(blackColor);
      /* Else erase the entry rect. */
      } else if (clearfirst) {
            EraseRect(&entryrect);
      }
      /* Fixes right end of the red frame. */
      if (GetCtlValue(list->hscrollbar) != GetCtlMax(list->hscrollbar))
          entryrect.right += 2;
      /* Draw a red frame around the selected unit. */
      if (list->contents->units[n].flag) {
            tmpcolor = (RGBColor) {0xFFFF, 0, 0};
            RGBForeColor(&tmpcolor);
            PenSize(2, 2);
            FrameRect(&entryrect);
            ForeColor(blackColor);
            PenNormal();
      }
      
      u = unit->type;
      /* Draw whether the unit is awake or asleep. */
      if (unit->plan && completed(unit)) {
            SetRect(&tmprect, 0, y + list->entryspacing/2 - curactcolw/2,
                        curactcolw, y + list->entryspacing/2 + curactcolw/2);
            InsetRect(&tmprect, 2, 2);
            /* (should draw the following analogously to map display) */
            if (unit->plan->asleep) {
                  tmpcolor = (RGBColor) {0xFFFF, 0, 0};
                  RGBForeColor(&tmpcolor);
                  FillRect(&tmprect, QDPat(black));
                  ForeColor(blackColor);
            } else if (unit->plan->reserve) {
                  tmpcolor = (RGBColor) {0xFFFF, 0, 0xFFFF};
                  RGBForeColor(&tmpcolor);
                  FillRect(&tmprect, QDPat(black));
                  ForeColor(blackColor);
            } else {
                  tmpcolor = (RGBColor) {0, 0xFFFF, 0};
                  RGBForeColor(&tmpcolor);
                  FillRect(&tmprect, QDPat(black));
                  ForeColor(blackColor);
            }
            FrameRect(&tmprect);
      }
      /* Draw an icon with side emblem for this unit. */
      draw_unit_image(list->window, curactcolw + 2, y + 1,
                              (list->largeicons ? 44 : 22), (list->largeicons ? 44 : 22), 
                              u, side_number(unit->side), list->sidecolors, 0, !completed(unit), list->draw_emblems);

      /* Draw unit size on top of advanced unit. */
      if (u_advanced(unit->type) && list->drawsizes) {
            draw_unit_size(unit, list->window, 
                  curactcolw + 2, y + 1, (list->largeicons ? 44 : 22), (list->largeicons ? 44 : 22));
      }
      /* Draw gray text for incomplete units, */
      if (!completed(unit)) {
            tmpcolor = (RGBColor) {0x8FFF, 0x8FFF, 0x8FFF};
            RGBForeColor(&tmpcolor);
      }
      /* Write the name or ordinal number. */
      name_or_number(unit, spbuf);
      spbuf[15] = '\0';  /* (should be clipping by pixels) */
      x = curactcolw + imagecolw;
      MoveTo(x, texty);
      DrawText(spbuf, 0, strlen(spbuf));
      /* Adjust clipping etc. according to the horizontal scroll. */
      x -= list->firstvisfield;
      cliprect.left = fixedfieldwidth;
      ClipRect(&cliprect);
      /* Write the name of the unit's type. */
      x += namecolw;
      MoveTo(x, texty);
      sprintf(spbuf, "%s", u_type_name(u));
      spbuf[15] = '\0';
      DrawText(spbuf, 0, strlen(spbuf));
      /* Write the side of the unit. */
      x += typecolw;
      MoveTo(x, texty);
      side2 = unit->side;
      strcpy(tmpnbuf, shortest_side_title(side2, spbuf));
      tmpnbuf[15] = '\0'; /* truncate long side names */
      DrawText(tmpnbuf, 0, strlen(tmpnbuf));
      /* Draw the current hit points of the unit. */
      x += sidecolw;
      if (side_sees_unit(dside, unit)) {
            MoveTo(x, texty);
            hp_desc(spbuf, unit, FALSE);
            DrawText(spbuf, 0, strlen(spbuf));
      }
      x += hpcolw;
      if (side_sees_unit(dside, unit) && inside_area(unit->x, unit->y)) {
            MoveTo(x, texty);
            if (!completed(unit)) {
                  sprintf(spbuf, "%d/%d", unit->cp, u_cp(u));
            } else acp_desc(spbuf, unit, FALSE);
            if (strlen(spbuf) == 0)
              strcpy(spbuf, "-");
            DrawText(spbuf, 0, strlen(spbuf));
      }
      /* Draw the location. */
      x += acpcolw;
      MoveTo(x, texty);
      if (inside_area(unit->x, unit->y)) {
            sprintf(spbuf, "%d,%d", unit->x, unit->y);
            if (unit->z != 0) {
                  tprintf(spbuf, ",%d", unit->z);
            }
      } else {
            strcpy(spbuf, "  -  ");
      }
      DrawText(spbuf, 0, strlen(spbuf));
      /* Draw the task. */
      x += poscolw;
      MoveTo(x, texty);
      if (unit->plan && unit->plan->tasks) {
            task_desc(spbuf, unit->side, unit, unit->plan->tasks);
      } else {
            strcpy(spbuf, "  -  ");
      }
      spbuf[25] = '\0';
      DrawText(spbuf, 0, strlen(spbuf));
      /* Draw the state of all the supplies. */
      x += taskcolw;
      if (side_sees_unit(dside, unit)) {
            for_all_material_types(m) {
                  if (sup_column_needed[m]) {
                        MoveTo(x, texty);
                        sprintf(spbuf, "%d", unit->supply[m]);
                        DrawText(spbuf, 0, strlen(spbuf));
                        x += supcolw;
                  }
            }
      } else {
            x += nummtypes * supcolw;
      }
      /* Draw extra notes. */
      MoveTo(x, texty);
      spbuf[0] = '\0';
      if (side_sees_unit(dside, unit)
            && !inside_area(unit->x, unit->y)
            && unit_appear_turn(unit) >= 0) {
            tprintf(spbuf, "appear %s", absolute_date_string(unit_appear_turn(unit)));
      }
      if (strlen(spbuf) > 0) {
            MoveTo(x, texty);
            DrawText(spbuf, 0, strlen(spbuf));
      }
      ForeColor(blackColor);
      /* Adjust clipping for vertical line. */
      cliprect = winrect;
      cliprect.right = fixedfieldwidth;
      cliprect.bottom -= sbarwid;
      ClipRect(&cliprect);
      /* Draw a dividing line between the fixed and mobile fields. */
      /* It may seem excessive to draw the whole line for each entry but 
      otherwise the line may end before the bottom of the window content. */
      MoveTo(fixedfieldwidth - 5, listtoph);
      LineTo(fixedfieldwidth - 5, winrect.bottom - sbarwid);
}

void
grow_list(List *list, int w, int h)
{
      WindowPtr listwin = list->window;

      SizeWindow(listwin, w, h, 1);
      list->firstvisfield = 0;
      adjust_list_decor(list);
      force_update(listwin);
}

/* Zooming works like a list view in the Finder - it calculates a "perfect" size,
   showing as much as possible but with no wasted blank areas. */

void
zoom_list(List *list, int part)
{
      int maxh;
      WindowPtr listwin = list->window;

      if (part == inZoomOut) {
            maxh = listtoph + list->numunits * list->entryspacing + sbarwid;
            set_standard_state(listwin, maxlistwidth + sbarwid, maxh);
      }
      ZoomWindow(listwin, part, false);
      list->firstvisfield = 0;
      adjust_list_decor(list);
      force_update(listwin);
}

/* Move and size the scrollbars to reflect the list's window. */

void
adjust_list_decor(List *list)
{
      int w, h;

      w = list->window->portRect.right - list->window->portRect.left;
      h = list->window->portRect.bottom - list->window->portRect.top;
      MoveControl(list->vscrollbar, w - sbarwid, listtoph - 1);
      SizeControl(list->vscrollbar, sbarwid + 1, h - listtoph - sbarwid + 2);
      MoveControl(list->hscrollbar, fixedfieldwidth - 5, h - sbarwid);
      SizeControl(list->hscrollbar, w - fixedfieldwidth + 5 - sbarwid + 2, sbarwid + 1);
      set_list_scrollbars(list);
}

/* Temporary used by scroll procs (saves looking up from the control). */

List *curlist;

static pascal void
list_vscroll_fn(ControlHandle control, short code)
{
      int curvalue, minvalue, maxvalue, oldvalue, pagesize, jump;
      RgnHandle tmprgn;
      Rect tmprect;
      WindowPtr listwin = curlist->window;

      curvalue = oldvalue = GetCtlValue(control);
      minvalue = GetCtlMin(control);
      maxvalue = GetCtlMax(control);
      pagesize = curlist->lastvisible - curlist->firstvisible + 1;
      switch (code) {
            case inPageDown:
                  jump = max(1, pagesize - 1);
                  break;
            case inDownButton:
                  jump = 1;
                  break;
            case inPageUp:
                  jump = min(-1, - (pagesize - 1));
                  break;
            case inUpButton:
                  jump = -1;
                  break;
            default:
                  jump = 0;
                  break;
      }
      curvalue = max(min(curvalue + jump, maxvalue), minvalue);
      curlist->firstvisible = curvalue;
      curlist->lastvisible = min(curlist->numunits, curlist->firstvisible + pagesize) - 1;
      SetCtlValue(control, curvalue);
      /* Scroll the already-drawn bits. */
      tmprgn = NewRgn();
      SetRect(&tmprect, 0, listtoph, listwin->portRect.right - sbarwid, listtoph + pagesize * curlist->entryspacing);
      ScrollRect(&tmprect, 0, (oldvalue - curvalue) * curlist->entryspacing, tmprgn);
      InvalRgn(tmprgn);
      /* Do the update now, because we won't get back to the main event loop
         until the mouse button is released. */
      update_window(curlist->window);
      DisposeRgn(tmprgn);
}

static pascal void
list_hscroll_fn(ControlHandle control, short code)
{
      int curvalue, minvalue, maxvalue, oldvalue, pagesize, jump;
      RgnHandle tmprgn;
      Rect tmprect;
      WindowPtr listwin = curlist->window;

      oldvalue = GetCtlValue(control);
      minvalue = GetCtlMin(control);
      maxvalue = GetCtlMax(control);

      pagesize = curlist->lastvisfield - curlist->firstvisfield;
      switch (code) {
            case inPageDown:
                  jump = 50;
                  break;
            case inDownButton:
                  jump = 10;
                  break;
            case inPageUp:
                  jump = -50;
                  break;
            case inUpButton:
                  jump = -10;
                  break;
            default:
                  jump = 0;
                  break;
      }
      curvalue = max(min(oldvalue + jump, maxvalue), minvalue);
      curlist->firstvisfield = curvalue;
      curlist->lastvisfield = curlist->firstvisfield + pagesize;
      SetCtlValue(control, curvalue);
      /* Scroll the already-drawn bits. */
      tmprgn = NewRgn();
      SetRect(&tmprect, fixedfieldwidth, 
                            0, 
                            listwin->portRect.right - sbarwid, 
                            listwin->portRect.bottom - sbarwid);
      ScrollRect(&tmprect, (oldvalue - curvalue), 0, tmprgn);
      InvalRgn(tmprgn);
      update_window(curlist->window);
      DisposeRgn(tmprgn);
}

/* Scroll up as long as up arrow key is pressed. */

void
auto_scroll_list_up_arrow()
{
      unsigned char keyMap[16];
      short keyDown = TRUE;

      while (keyDown) {
            list_vscroll_fn(curlist->vscrollbar, inUpButton);
            GetKeys((GETKEYS_ARG_TYPE) keyMap);
            keyDown = keyMap[0x7E >> 3] >> (0x7E & 7) & 1;  
      }
}

/* Scroll down as long as down key is pressed. */

void
auto_scroll_list_down_arrow()
{
      unsigned char keyMap[16];
      short keyDown = TRUE;

      while (keyDown) {
            list_vscroll_fn(curlist->vscrollbar, inDownButton);
            GetKeys((GETKEYS_ARG_TYPE) keyMap);
            keyDown = keyMap[0x7D >> 3] >> (0x7D & 7) & 1;
      }
}

/* Scroll left as long as left arrow key is pressed */

void
auto_scroll_list_left_arrow()
{
      unsigned char keyMap[16];
      short keyDown = TRUE;

      while (keyDown) {
            list_hscroll_fn(curlist->hscrollbar, inUpButton);
            GetKeys((GETKEYS_ARG_TYPE) keyMap);
            keyDown = keyMap[0x7B >> 3] >> (0x7B & 7) & 1;
      }
}

/* Scroll right as long as right arrow key is pressed. */

void
auto_scroll_list_right_arrow()
{
      unsigned char keyMap[16];
      short keyDown = TRUE;

      while (keyDown) {
            list_hscroll_fn(curlist->hscrollbar, inDownButton);
            GetKeys((GETKEYS_ARG_TYPE) keyMap);
            keyDown = keyMap[0x7C >> 3] >> (0x7C & 7) & 1;
      }
}


int
do_key_down_list(List *list, char key)
{
      int         handled = TRUE;
      GrafPtr     oldport;

      GetPort(&oldport);
      SetPort(list->window);
      curlist = list;

      switch (key) {
            case 27:          /* Escape key. */
                  close_window(curlist->window);
                  break;
            case 0x1F:        /* Down Arrow. */
                  auto_scroll_list_down_arrow();
                  break;
            case 0x1E:        /* Up Arrow. */
                  auto_scroll_list_up_arrow();
                  break;
            case 0x1D:        /* Right Arrow. */
                  auto_scroll_list_right_arrow();
                  break;
            case 0x1C:        /* Left Arrow. */
                  auto_scroll_list_left_arrow();
                  break;
            case 0x0C:        /* Page Down. */
                  list_vscroll_fn(curlist->vscrollbar, inPageDown);
                  break;
            case 0x0B:        /* Page Up. */
                  list_vscroll_fn(curlist->vscrollbar, inPageUp);
                  break;
            default:
                  /* The key was not handled here. */
                  handled = FALSE;
                  break;
      }
      SetPort(oldport);
      return handled;
}

/* Handle a mouse down in the list.  Grafport already set, mouse coords are local. */

/* (mouse downs should select/deselect list elements) */

void
do_mouse_down_list(List *list, Point mouse, int mods)
{
      ControlHandle control;
      short part;
      int n, tmp;
      WindowPtr window = list->window;
      Rect winrect = window->portRect;

      if (list_vscroll_proc == NULL)
        list_vscroll_proc = NewControlActionProc(list_vscroll_fn);
      if (list_hscroll_proc == NULL)
        list_hscroll_proc = NewControlActionProc(list_hscroll_fn);

      part = FindControl(mouse, window, &control);
      if (control == list->vscrollbar) {
            switch (part) {
                  case inThumb:
                        part = TrackControl(control, mouse, NULL);
                        if (part == inThumb) {
                              list->firstvisible = GetCtlValue(control);
                              force_update(window);
                        }
                        break;
                  default:
                        curlist = list;
                        part = TrackControl(control, mouse, list_vscroll_proc);
                        list->firstvisible = GetCtlValue(control);
                        break;
            }
      } else if (control == list->hscrollbar) {
            switch (part) {
                  case inThumb:
                        part = TrackControl(control, mouse, NULL);
                        if (part == inThumb) {
                              list->firstvisfield = GetCtlValue(control);
                              force_update(window);
                        }
                        break;
                  default:
                        curlist = list;
                        part = TrackControl(control, mouse, list_hscroll_proc);
                        list->firstvisfield = GetCtlValue(control);
                        break;
            }
      } else {
            if (mouse.v < listtoph) {
                  if (between(curactcolw, mouse.h, curactcolw + imagecolw)) {
                        toggle_list_large_icons(list);
                  }
            /* Ignore clicks in the empty box to the left of the bottom scrollbar. */
            } else if (mouse.v < winrect.bottom - sbarwid) {
                  /* Figure out the selected unit. */
                  n = (mouse.v - listtoph) / list->entryspacing + list->firstvisible;
                  tmp = list->contents->units[n].flag;
                  clear_selections(list);
                  /* Don't draw units below lastvisible. */
                  if (n <= list->lastvisible) {
                        list->contents->units[n].flag = !tmp;
                        redraw_unit_list_entry(list, n);
                        /* Select the unit on frontmap and make it the current unit if possible. */
                        if (frontmap) {
                              Unit *unit = selected_unit_in_list(list);

                              select_exactly_one_unit(frontmap, unit);
                              if (side_controls_unit(dside, unit)
                                  && could_be_next_unit(unit)) {
                                    frontmap->curunit = unit;
                              }
                        }
                  }
            }
      }
}

void
set_list_sorting(List *list, enum sortkeys newkey, int mi)
{
      int i;
      
      if (newkey != list->sortkeys[0]) {
            /* Push all the existing sortkeys back - this way they'll can be
               used as tiebreakers for the new sort key. */
            for (i = MAXSORTKEYS - 1; i > 0; --i) {
                  list->sortkeys[i] = list->sortkeys[i - 1];
            }
            /* Add the new one onto the front. */
            list->sortkeys[0] = newkey;
            sort_list_contents(list);
            force_update(list->window);
            /* Record the menu item so it can get a checkmark during menu adjust. */
            list->mainsortmi = mi;
      }
}

void
toggle_list_completed(List *list)
{
      list->completed_units = !list->completed_units;
      reorganize_list(list);
}

void
toggle_list_incomplete(List *list)
{
      list->incomplete_units = !list->incomplete_units;
      reorganize_list(list);
}

void
toggle_list_large_icons(List *list)
{
      int delta = largeentryspacing - smallentryspacing;

      list->largeicons = !list->largeicons;
      list->entryspacing = (list->largeicons ? largeentryspacing : smallentryspacing);
      fixedfieldwidth += (list->largeicons ? delta : -delta); 
      adjust_list_decor(list);
      force_update(list->window);
}

void
toggle_list_sidecolors(List *list)
{
      list->sidecolors = !list->sidecolors;
      force_update(list->window);
}

void
toggle_list_draw_emblems(List *list)
{
      list->draw_emblems = !list->draw_emblems;
      force_update(list->window);
}

void
update_unit_in_lists(Unit *unit)
{
      List  *list;
      int   i, line;
       
      for_all_lists(list) {
            line = unit_position_in_list(list, unit);
            /* The unit is not in the list. */
            if (line < 0) {
                  /* Insert the unit into the list if it should be there. */
                  if (in_play(unit)
                       && (completed(unit) ? list->completed_units : list->incomplete_units)) {
                        insert_unit_into_list(list, unit);        
                  }
            /* The unit is in the list and should stay there. */
            } else if (in_play(unit)
                       && (completed(unit) ? list->completed_units : list->incomplete_units)) {
                  /* Check if the unit should be moved within the list. */
                  organize_list_contents(list);
                  /* The unit did not move. Just redraw the entry. */
                  if (unit_position_in_list(list, unit) == line) {
                        /* Only redraw the entry if it is visible. */
                        if (between(list->firstvisible, line, list->lastvisible)) {
                              redraw_unit_list_entry(list, line);
                        }
                  /* The unit moved within the list. Redraw the list. */
                  } else {

#if 0             /* This code still misses some necessary updates ... */

                        /* Find the first line from which the list needs to be redrawn. */
                        line = min(line, unit_position_in_list(list, unit));
                        line = max(line, list->firstvisible);
                        /* Only redraw those entries that are visible. */
                        if (line <= list->lastvisible) {
                              for (i = line; i <= list->lastvisible; ++i)
                                    redraw_unit_list_entry(list, i);
                        }

#else       /* ... so we use brute force for the time being. */

                        force_update(list->window);
#endif

                  }
            /* Else remove the unit from the list. */
            } else remove_unit_from_list(list, unit);
      }
}

void
insert_unit_into_list(List *list, Unit *unit)
{
      int line, i;

      /* We assume that the given unit is the only one that is added to
      or removed from the list. */
      organize_list_contents(list);
      /* Find the new unit's position. */
      line = unit_position_in_list(list, unit);
      /* Update all visible lines below the new unit's position. */
      line = max(line, list->firstvisible);
      if (line <= list->lastvisible) {
            for (i = line; i <= list->lastvisible; ++i)
                  redraw_unit_list_entry(list, i);
      } 
}

void
remove_unit_from_list(List *list, Unit *unit)
{
      int line, i;

      /* Save the unit's old position. */
      line = unit_position_in_list(list, unit);
      /* We assume that the given unit is the only one that is added to
      or removed from the list. */
      organize_list_contents(list);
      /* Update all visible lines below the unit's old position. */
      line = max(line, list->firstvisible);
      if (line <= list->lastvisible) {
            for (i = line; i <= list->lastvisible; ++i)
                  redraw_unit_list_entry(list, i);
      } 
}

int
unit_position_in_list(List *list, Unit *unit)
{
      int i;
      
      for (i = 0; i < list->numunits; ++i) {
            if (unit == unit_in_vector(list->contents, i))
              return i;
      }
      return (-1);
}

void
reorganize_list(List *list)
{
      organize_list_contents(list);
      force_update(list->window);
}

void
redraw_unit_list_entry(List *list, int n)
{
      WindowPtr listwin;
      RgnHandle tmprgn;
      GrafPtr oldport;
      Unit *unit;

      if (!active_display(dside) || list == NULL)
          return;
      listwin = list->window;
      GetPort(&oldport);
      SetPort(listwin);
      tmprgn = NewRgn();
      GetClip(tmprgn);
      draw_unit_list_entry(list, n, TRUE);
      SetClip(tmprgn);
      DisposeRgn(tmprgn);
      SetPort(oldport);
}

void
clear_selections(List *list)
{
      int i;
      
      for (i = 0; i < list->numunits; ++i) {
            if (list->contents->units[i].flag) {
                  list->contents->units[i].flag = FALSE;
                  redraw_unit_list_entry(list, i);
            }
      }
}

Unit *
selected_unit_in_list(List *list)
{
      int i;
      
      for (i = 0; i < list->numunits; ++i) {
            if (list->contents->units[i].flag)
              return unit_in_vector(list->contents, i);
      }
      return NULL;
}

/* This finds a good map to scroll over to look at a unit mentioned in the list. */

void
scroll_to_selected_unit_in_list(List *list)
{
      Unit *unit;

      /* Beep and return if there are no maps open currently. */
      if (maplist == NULL) {
            beep();
            return;
      }
      unit = selected_unit_in_list(list);
      if (unit != NULL && inside_area(unit->x, unit->y))
        scroll_best_map_to_unit(unit, FALSE);
}

void
activate_list(List *list, int activate)
{
      Rect growRect;

      GrafPtr oldport;

      if (activate) {
            HiliteControl(list->vscrollbar, 0);
            HiliteControl(list->hscrollbar, 0);
      } else {
            HiliteControl(list->vscrollbar, 255);
            HiliteControl(list->hscrollbar, 255);
      }
}

void
print_list(List *list)
{
#if 0
      TPPrPort printport;
      extern THPrint printrecordhandle;

      printport = PrOpenDoc(printrecordhandle, nil, nil);
      PrCloseDoc(printport);
#endif
}

/* Remove and destroy the list object. */

void
destroy_list(List *list)
{
      List *list2;
      
      if (listlist == list) {
            listlist = list->next;
      } else {
            for_all_lists(list2) {
                  if (list2->next == list) {
                        list2->next = list->next;
                  }
                  if (list2->id > list->id) {
                        remove_window_menu_item(list2->window);
                        --list2->id;
                        sprintf(spbuf, "List %d", list2->id);
                        add_window_menu_item(spbuf, list2->window);
                  }
            }
      }
      --listnum;
      /* (should destroy substructs) */
      free(list);
}


Generated by  Doxygen 1.6.0   Back to index