446 { WWT_RESIZEBOX, RESIZE_TB, 13, 196, 207, 190, 201, 0x0, STR_RESIZE_BUTTON}, |
449 { WWT_RESIZEBOX, RESIZE_TB, 13, 196, 207, 190, 201, 0x0, STR_RESIZE_BUTTON}, |
447 { WIDGETS_END}, |
450 { WIDGETS_END}, |
448 }; |
451 }; |
449 |
452 |
450 |
453 |
451 /* used to get a sorted list of the towns */ |
|
452 static uint _num_town_sort; |
|
453 |
|
454 static char _bufcache[64]; |
|
455 static const Town* _last_town; |
|
456 |
|
457 static int CDECL TownNameSorter(const void *a, const void *b) |
|
458 { |
|
459 const Town* ta = *(const Town**)a; |
|
460 const Town* tb = *(const Town**)b; |
|
461 char buf1[64]; |
|
462 int r; |
|
463 |
|
464 SetDParam(0, ta->index); |
|
465 GetString(buf1, STR_TOWN, lastof(buf1)); |
|
466 |
|
467 /* If 'b' is the same town as in the last round, use the cached value |
|
468 * We do this to speed stuff up ('b' is called with the same value a lot of |
|
469 * times after eachother) */ |
|
470 if (tb != _last_town) { |
|
471 _last_town = tb; |
|
472 SetDParam(0, tb->index); |
|
473 GetString(_bufcache, STR_TOWN, lastof(_bufcache)); |
|
474 } |
|
475 |
|
476 r = strcmp(buf1, _bufcache); |
|
477 if (_town_sort_order & 1) r = -r; |
|
478 return r; |
|
479 } |
|
480 |
|
481 static int CDECL TownPopSorter(const void *a, const void *b) |
|
482 { |
|
483 const Town* ta = *(const Town**)a; |
|
484 const Town* tb = *(const Town**)b; |
|
485 int r = ta->population - tb->population; |
|
486 if (_town_sort_order & 1) r = -r; |
|
487 return r; |
|
488 } |
|
489 |
|
490 static void MakeSortedTownList() |
|
491 { |
|
492 const Town* t; |
|
493 uint n = 0; |
|
494 |
|
495 /* Create array for sorting */ |
|
496 _town_sort = ReallocT(_town_sort, GetMaxTownIndex() + 1); |
|
497 |
|
498 FOR_ALL_TOWNS(t) _town_sort[n++] = t; |
|
499 |
|
500 _num_town_sort = n; |
|
501 |
|
502 _last_town = NULL; // used for "cache" |
|
503 qsort((void*)_town_sort, n, sizeof(_town_sort[0]), _town_sort_order & 2 ? TownPopSorter : TownNameSorter); |
|
504 |
|
505 DEBUG(misc, 3, "Resorting towns list"); |
|
506 } |
|
507 |
|
508 |
|
509 struct TownDirectoryWindow : public Window { |
454 struct TownDirectoryWindow : public Window { |
510 private: |
455 private: |
511 enum TownDirectoryWidget { |
456 enum TownDirectoryWidget { |
512 TDW_SORTNAME = 3, |
457 TDW_SORTNAME = 3, |
513 TDW_SORTPOPULATION, |
458 TDW_SORTPOPULATION, |
514 TDW_CENTERTOWN, |
459 TDW_CENTERTOWN, |
515 }; |
460 }; |
516 |
461 |
|
462 /* Runtime saved values */ |
|
463 static Listing last_sorting; |
|
464 static const Town *last_town; |
|
465 |
|
466 /* Constants for sorting towns */ |
|
467 static const GUITownList::SortFunction * const sorter_funcs[]; |
|
468 |
|
469 GUITownList towns; |
|
470 |
|
471 void BuildTownList() |
|
472 { |
|
473 if (!this->towns.NeedRebuild()) return; |
|
474 |
|
475 this->towns.Clear(); |
|
476 |
|
477 const Town *t; |
|
478 FOR_ALL_TOWNS(t) { |
|
479 *this->towns.Append() = t; |
|
480 } |
|
481 |
|
482 this->towns.Compact(); |
|
483 this->towns.RebuildDone(); |
|
484 } |
|
485 |
|
486 void SortTownList() |
|
487 { |
|
488 last_town = NULL; |
|
489 this->towns.Sort(); |
|
490 } |
|
491 |
|
492 /** Sort by town name */ |
|
493 static int CDECL TownNameSorter(const Town * const *a, const Town * const *b) |
|
494 { |
|
495 static char buf_cache[64]; |
|
496 const Town *ta = *a; |
|
497 const Town *tb = *b; |
|
498 char buf[64]; |
|
499 |
|
500 SetDParam(0, ta->index); |
|
501 GetString(buf, STR_TOWN, lastof(buf)); |
|
502 |
|
503 /* If 'b' is the same town as in the last round, use the cached value |
|
504 * We do this to speed stuff up ('b' is called with the same value a lot of |
|
505 * times after eachother) */ |
|
506 if (tb != last_town) { |
|
507 last_town = tb; |
|
508 SetDParam(0, tb->index); |
|
509 GetString(buf_cache, STR_TOWN, lastof(buf_cache)); |
|
510 } |
|
511 |
|
512 return strcmp(buf, buf_cache); |
|
513 } |
|
514 |
|
515 /** Sort by population */ |
|
516 static int CDECL TownPopulationSorter(const Town * const *a, const Town * const *b) |
|
517 { |
|
518 return (*a)->population - (*b)->population; |
|
519 } |
|
520 |
517 public: |
521 public: |
518 TownDirectoryWindow(const WindowDesc *desc) : Window(desc, 0) |
522 TownDirectoryWindow(const WindowDesc *desc) : Window(desc, 0) |
519 { |
523 { |
520 this->vscroll.cap = 16; |
524 this->vscroll.cap = 16; |
521 this->resize.step_height = 10; |
525 this->resize.step_height = 10; |
522 this->resize.height = this->height - 10 * 6; // minimum of 10 items in the list, each item 10 high |
526 this->resize.height = this->height - 10 * 6; // minimum of 10 items in the list, each item 10 high |
523 |
527 |
|
528 this->towns.SetListing(this->last_sorting); |
|
529 this->towns.SetSortFuncs(this->sorter_funcs); |
|
530 this->towns.ForceRebuild(); |
|
531 |
524 this->FindWindowPlacementAndResize(desc); |
532 this->FindWindowPlacementAndResize(desc); |
525 } |
533 } |
526 |
534 |
|
535 ~TownDirectoryWindow() |
|
536 { |
|
537 this->last_sorting = this->towns.GetListing(); |
|
538 } |
|
539 |
527 virtual void OnPaint() |
540 virtual void OnPaint() |
528 { |
541 { |
529 if (_town_sort_dirty) { |
542 this->BuildTownList(); |
530 _town_sort_dirty = false; |
543 this->SortTownList(); |
531 MakeSortedTownList(); |
544 |
532 } |
545 SetVScrollCount(this, this->towns.Length()); |
533 |
|
534 SetVScrollCount(this, _num_town_sort); |
|
535 |
546 |
536 this->DrawWidgets(); |
547 this->DrawWidgets(); |
537 this->DrawSortButtonState((_town_sort_order <= 1) ? TDW_SORTNAME : TDW_SORTPOPULATION, _town_sort_order & 1 ? SBS_DOWN : SBS_UP); |
548 this->DrawSortButtonState(this->towns.sort_type == 0 ? TDW_SORTNAME : TDW_SORTPOPULATION, this->towns.IsDescSortOrder() ? SBS_DOWN : SBS_UP); |
538 |
549 |
539 { |
550 { |
540 int n = 0; |
551 int n = 0; |
541 uint16 i = this->vscroll.pos; |
552 uint16 i = this->vscroll.pos; |
542 int y = 28; |
553 int y = 28; |
543 |
554 |
544 while (i < _num_town_sort) { |
555 while (i < this->towns.Length()) { |
545 const Town* t = _town_sort[i]; |
556 const Town *t = this->towns[i]; |
546 |
557 |
547 assert(t->xy); |
558 assert(t->xy); |
548 |
559 |
549 SetDParam(0, t->index); |
560 SetDParam(0, t->index); |
550 SetDParam(1, t->population); |
561 SetDParam(1, t->population); |
562 |
573 |
563 virtual void OnClick(Point pt, int widget) |
574 virtual void OnClick(Point pt, int widget) |
564 { |
575 { |
565 switch (widget) { |
576 switch (widget) { |
566 case TDW_SORTNAME: /* Sort by Name ascending/descending */ |
577 case TDW_SORTNAME: /* Sort by Name ascending/descending */ |
567 _town_sort_order = (_town_sort_order == 0) ? 1 : 0; |
578 if (this->towns.SortType() == 0) { |
568 _town_sort_dirty = true; |
579 this->towns.ToggleSortOrder(); |
|
580 } else { |
|
581 this->towns.SetSortType(0); |
|
582 } |
569 this->SetDirty(); |
583 this->SetDirty(); |
570 break; |
584 break; |
571 |
585 |
572 case TDW_SORTPOPULATION: /* Sort by Population ascending/descending */ |
586 case TDW_SORTPOPULATION: /* Sort by Population ascending/descending */ |
573 _town_sort_order = (_town_sort_order == 2) ? 3 : 2; |
587 if (this->towns.SortType() == 1) { |
574 _town_sort_dirty = true; |
588 this->towns.ToggleSortOrder(); |
|
589 } else { |
|
590 this->towns.SetSortType(1); |
|
591 } |
575 this->SetDirty(); |
592 this->SetDirty(); |
576 break; |
593 break; |
577 |
594 |
578 case TDW_CENTERTOWN: { /* Click on Town Matrix */ |
595 case TDW_CENTERTOWN: { /* Click on Town Matrix */ |
579 const Town* t; |
|
580 |
|
581 uint16 id_v = (pt.y - 28) / 10; |
596 uint16 id_v = (pt.y - 28) / 10; |
582 |
597 |
583 if (id_v >= this->vscroll.cap) return; // click out of bounds |
598 if (id_v >= this->vscroll.cap) return; // click out of bounds |
584 |
599 |
585 id_v += this->vscroll.pos; |
600 id_v += this->vscroll.pos; |
586 |
601 |
587 if (id_v >= _num_town_sort) return; // click out of town bounds |
602 if (id_v >= this->towns.Length()) return; // click out of town bounds |
588 |
603 |
589 t = _town_sort[id_v]; |
604 const Town *t = this->towns[id_v]; |
590 assert(t->xy); |
605 assert(t->xy); |
591 if (_ctrl_pressed) { |
606 if (_ctrl_pressed) { |
592 ShowExtraViewPortWindow(t->xy); |
607 ShowExtraViewPortWindow(t->xy); |
593 } else { |
608 } else { |
594 ScrollMainWindowToTile(t->xy); |
609 ScrollMainWindowToTile(t->xy); |
605 |
620 |
606 virtual void OnResize(Point new_size, Point delta) |
621 virtual void OnResize(Point new_size, Point delta) |
607 { |
622 { |
608 this->vscroll.cap += delta.y / 10; |
623 this->vscroll.cap += delta.y / 10; |
609 } |
624 } |
|
625 |
|
626 virtual void OnInvalidateData(int data) |
|
627 { |
|
628 if (data == 0) { |
|
629 this->towns.ForceRebuild(); |
|
630 } else { |
|
631 this->towns.ForceResort(); |
|
632 } |
|
633 } |
|
634 }; |
|
635 |
|
636 Listing TownDirectoryWindow::last_sorting = {false, 0}; |
|
637 const Town *TownDirectoryWindow::last_town = NULL; |
|
638 |
|
639 /* Available town directory sorting functions */ |
|
640 const GUITownList::SortFunction * const TownDirectoryWindow::sorter_funcs[] = { |
|
641 &TownNameSorter, |
|
642 &TownPopulationSorter, |
610 }; |
643 }; |
611 |
644 |
612 static const WindowDesc _town_directory_desc = { |
645 static const WindowDesc _town_directory_desc = { |
613 WDP_AUTO, WDP_AUTO, 208, 202, 208, 202, |
646 WDP_AUTO, WDP_AUTO, 208, 202, 208, 202, |
614 WC_TOWN_DIRECTORY, WC_NONE, |
647 WC_TOWN_DIRECTORY, WC_NONE, |