comparison src/savedstatuses.c @ 12724:10ef9171ca87

[gaim-migrate @ 15068] Don't have a separate popular status list. That was stupid. Instead, keep the saved_status GList sorted by popularity. This should be faster for the few things that it affects, and it fixes the bug where the docklet wouldn't show popular statuses at startup committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Thu, 05 Jan 2006 00:11:20 +0000
parents d03afaccd41c
children d3232d64fafd
comparison
equal deleted inserted replaced
12723:20f5daedcccb 12724:10ef9171ca87
75 const GaimStatusType *type; 75 const GaimStatusType *type;
76 char *message; 76 char *message;
77 }; 77 };
78 78
79 static GList *saved_statuses = NULL; 79 static GList *saved_statuses = NULL;
80 static GList *popular_statuses = NULL;
81 static guint save_timer = 0; 80 static guint save_timer = 0;
82 static gboolean statuses_loaded = FALSE; 81 static gboolean statuses_loaded = FALSE;
83 82
84 /* 83 /*
85 * This hash table keeps track of which timestamps we've 84 * This hash table keeps track of which timestamps we've
97 /********************************************************************* 96 /*********************************************************************
98 * Private utility functions * 97 * Private utility functions *
99 *********************************************************************/ 98 *********************************************************************/
100 99
101 static void 100 static void
102 free_statussavedsub(GaimSavedStatusSub *substatus) 101 free_saved_status_sub(GaimSavedStatusSub *substatus)
103 { 102 {
104 g_return_if_fail(substatus != NULL); 103 g_return_if_fail(substatus != NULL);
105 104
106 g_free(substatus->message); 105 g_free(substatus->message);
107 g_free(substatus); 106 g_free(substatus);
108 } 107 }
109 108
110 static void 109 static void
111 free_statussaved(GaimSavedStatus *status) 110 free_saved_status(GaimSavedStatus *status)
112 { 111 {
113 g_return_if_fail(status != NULL); 112 g_return_if_fail(status != NULL);
114 113
115 g_free(status->title); 114 g_free(status->title);
116 g_free(status->message); 115 g_free(status->message);
117 116
118 while (status->substatuses != NULL) 117 while (status->substatuses != NULL)
119 { 118 {
120 GaimSavedStatusSub *substatus = status->substatuses->data; 119 GaimSavedStatusSub *substatus = status->substatuses->data;
121 status->substatuses = g_list_remove(status->substatuses, substatus); 120 status->substatuses = g_list_remove(status->substatuses, substatus);
122 free_statussavedsub(substatus); 121 free_saved_status_sub(substatus);
123 } 122 }
124 123
125 g_free(status); 124 g_free(status);
126 } 125 }
127 126
143 g_hash_table_insert(creation_times, 142 g_hash_table_insert(creation_times,
144 &status->creation_time, 143 &status->creation_time,
145 status); 144 status);
146 } 145 }
147 146
147 /**
148 * A magic number is calcuated for each status, and then the
149 * statuses are ordered by the magic number. The magic number
150 * is the date the status was last used offset by one day for
151 * each time the status has been used (but only by 10 days at
152 * the most).
153 *
154 * The goal is to have recently used statuses at the top of
155 * the list, but to also keep frequently used statuses near
156 * the top.
157 */
148 static gint 158 static gint
149 compare_times(gconstpointer a, gconstpointer b) 159 saved_statuses_sort_func(gconstpointer a, gconstpointer b)
150 { 160 {
151 const time_t timea = *((time_t *)a); 161 const GaimSavedStatus *saved_status_a = a;
152 const time_t timeb = *((time_t *)b); 162 const GaimSavedStatus *saved_status_b = b;
153 if (timea > timeb) 163 time_t time_a = saved_status_a->lastused +
164 (MIN(saved_status_a->usage_count, 10) * 86400);
165 time_t time_b = saved_status_b->lastused +
166 (MIN(saved_status_b->usage_count, 10) * 86400);
167 if (time_a > time_b)
154 return -1; 168 return -1;
155 if (timea < timeb) 169 if (time_a < time_b)
156 return 1; 170 return 1;
157 return 0; 171 return 0;
158 } 172 }
159 173
160 /** 174 /**
164 * does the expiration. 178 * does the expiration.
165 */ 179 */
166 static void 180 static void
167 remove_old_transient_statuses() 181 remove_old_transient_statuses()
168 { 182 {
169 GList *lastused;
170 time_t threshold;
171 time_t creation_time;
172 GList *l, *next; 183 GList *l, *next;
173 GaimSavedStatus *saved_status; 184 GaimSavedStatus *saved_status;
174 int i; 185 int count;
175 186 time_t creation_time;
176 /* Construct a GList containing the lastused times from each transient status */ 187
177 lastused = NULL; 188 /*
178 for (l = saved_statuses; l != NULL; l = l->next) 189 * Iterate through the list of saved statuses. Delete all
179 { 190 * transient statuses except for the first MAX_TRANSIENTS
191 * (remember, the saved statuses are already sorted by popularity).
192 */
193 count = 0;
194 for (l = saved_statuses; l != NULL; l = next)
195 {
196 next = l->next;
180 saved_status = l->data; 197 saved_status = l->data;
181 if (gaim_savedstatus_is_transient(saved_status)) 198 if (gaim_savedstatus_is_transient(saved_status))
182 lastused = g_list_insert_sorted(lastused, &saved_status->lastused, compare_times);
183 }
184
185 /* Find the 5th most recently used transient status */
186 l = lastused;
187 i = 0;
188 while ((l != NULL) && (i < MAX_TRANSIENTS))
189 {
190 l = l->next;
191 i++;
192 }
193 if (l != NULL)
194 threshold = *((time_t *)l->data);
195 else
196 threshold = 0;
197 g_list_free(lastused);
198
199 if (threshold == 0)
200 /* We have 5 or fewer transient statuses, so there is nothing to delete */
201 return;
202
203 /* Delete all transient statuses older than the threshold */
204 for (l = saved_statuses; l != NULL; l = next)
205 {
206 next = l->next;
207 saved_status = l->data;
208 if (gaim_savedstatus_is_transient(saved_status) && (saved_status->lastused <= threshold))
209 { 199 {
210 saved_statuses = g_list_remove(saved_statuses, saved_status); 200 if (count == MAX_TRANSIENTS)
211 creation_time = gaim_savedstatus_get_creation_time(saved_status); 201 {
212 g_hash_table_remove(creation_times, &creation_time); 202 saved_statuses = g_list_remove(saved_statuses, saved_status);
213 free_statussaved(saved_status); 203 creation_time = gaim_savedstatus_get_creation_time(saved_status);
204 g_hash_table_remove(creation_times, &creation_time);
205 free_saved_status(saved_status);
206 }
207 else
208 count++;
214 } 209 }
215 } 210 }
216 schedule_save(); 211
212 if (count == MAX_TRANSIENTS)
213 schedule_save();
217 } 214 }
218 215
219 /********************************************************************* 216 /*********************************************************************
220 * Writing to disk * 217 * Writing to disk *
221 *********************************************************************/ 218 *********************************************************************/
519 { 516 {
520 GaimSavedStatus *new; 517 GaimSavedStatus *new;
521 new = parse_status(status); 518 new = parse_status(status);
522 saved_statuses = g_list_prepend(saved_statuses, new); 519 saved_statuses = g_list_prepend(saved_statuses, new);
523 } 520 }
521 saved_statuses = g_list_sort(saved_statuses, saved_statuses_sort_func);
524 522
525 xmlnode_free(statuses); 523 xmlnode_free(statuses);
526 } 524 }
527 525
528 526
541 status = g_new0(GaimSavedStatus, 1); 539 status = g_new0(GaimSavedStatus, 1);
542 status->title = g_strdup(title); 540 status->title = g_strdup(title);
543 status->type = type; 541 status->type = type;
544 set_creation_time(status, time(NULL)); 542 set_creation_time(status, time(NULL));
545 543
546 saved_statuses = g_list_prepend(saved_statuses, status); 544 saved_statuses = g_list_insert_sorted(saved_statuses, status, saved_statuses_sort_func);
547 545
548 schedule_save(); 546 schedule_save();
549 547
550 return status; 548 return status;
551 } 549 }
648 return FALSE; 646 return FALSE;
649 647
650 saved_statuses = g_list_remove(saved_statuses, status); 648 saved_statuses = g_list_remove(saved_statuses, status);
651 creation_time = gaim_savedstatus_get_creation_time(status); 649 creation_time = gaim_savedstatus_get_creation_time(status);
652 g_hash_table_remove(creation_times, &creation_time); 650 g_hash_table_remove(creation_times, &creation_time);
653 free_statussaved(status); 651 free_saved_status(status);
654 652
655 schedule_save(); 653 schedule_save();
656 654
657 /* 655 /*
658 * If we just deleted our current status or our idleaway status, 656 * If we just deleted our current status or our idleaway status,
673 gaim_savedstatuses_get_all(void) 671 gaim_savedstatuses_get_all(void)
674 { 672 {
675 return saved_statuses; 673 return saved_statuses;
676 } 674 }
677 675
678 /**
679 * A magic number is calcuated for each status, and then the
680 * statuses are ordered by the magic number. The magic number
681 * is the date the status was last used offset by one day for
682 * each time the status has been used (but only by 10 days at
683 * the most).
684 *
685 * The goal is to have recently used statuses at the top of
686 * the list, but to also keep frequently used statuses near
687 * the top.
688 */
689 static gint
690 popular_statuses_compare_func(gconstpointer a, gconstpointer b)
691 {
692 const GaimSavedStatus *saved_status_a = a;
693 const GaimSavedStatus *saved_status_b = b;
694 time_t time_a = saved_status_a->lastused +
695 (MIN(saved_status_a->usage_count, 10) * 86400);
696 time_t time_b = saved_status_b->lastused +
697 (MIN(saved_status_b->usage_count, 10) * 86400);
698 if (time_a > time_b)
699 return -1;
700 if (time_a < time_b)
701 return 1;
702 return 0;
703 }
704
705 GList * 676 GList *
706 gaim_savedstatuses_get_popular(unsigned int how_many) 677 gaim_savedstatuses_get_popular(unsigned int how_many)
707 { 678 {
708 GList *truncated = NULL; 679 GList *truncated = NULL;
709 GList *cur; 680 GList *cur;
710 int i; 681 int i;
711 682
712 /* Copy 'how_many' elements to a new list */ 683 /* Copy 'how_many' elements to a new list */
713 for (i = 0, cur = popular_statuses; (i < how_many) && (cur != NULL); i++) 684 for (i = 0, cur = saved_statuses; (i < how_many) && (cur != NULL); i++)
714 { 685 {
715 truncated = g_list_append(truncated, cur->data); 686 truncated = g_list_append(truncated, cur->data);
716 cur = cur->next; 687 cur = cur->next;
717 } 688 }
718 689
940 911
941 saved_status->lastused = time(NULL); 912 saved_status->lastused = time(NULL);
942 gaim_prefs_set_int("/core/savedstatus/current", 913 gaim_prefs_set_int("/core/savedstatus/current",
943 gaim_savedstatus_get_creation_time(saved_status)); 914 gaim_savedstatus_get_creation_time(saved_status));
944 915
945 /* Update our list of popular statuses */ 916 /* Make sure our list of saved statuses remains sorted */
946 saved_status->usage_count++; 917 saved_status->usage_count++;
947 g_list_free(popular_statuses); 918 saved_statuses = g_list_remove(saved_statuses, saved_status);
948 popular_statuses = g_list_copy(saved_statuses); 919 saved_statuses = g_list_insert_sorted(saved_statuses, saved_status, saved_statuses_sort_func);
949 popular_statuses = g_list_sort(popular_statuses,
950 popular_statuses_compare_func);
951 920
952 } 921 }
953 922
954 void 923 void
955 gaim_savedstatus_activate_for_account(const GaimSavedStatus *saved_status, 924 gaim_savedstatus_activate_for_account(const GaimSavedStatus *saved_status,
1029 } 998 }
1030 999
1031 while (saved_statuses != NULL) { 1000 while (saved_statuses != NULL) {
1032 GaimSavedStatus *saved_status = saved_statuses->data; 1001 GaimSavedStatus *saved_status = saved_statuses->data;
1033 saved_statuses = g_list_remove(saved_statuses, saved_status); 1002 saved_statuses = g_list_remove(saved_statuses, saved_status);
1034 free_statussaved(saved_status); 1003 free_saved_status(saved_status);
1035 } 1004 }
1036 1005
1037 g_hash_table_destroy(creation_times); 1006 g_hash_table_destroy(creation_times);
1038 } 1007 }
1039 1008