Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/src/summary.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 132 - (show annotations) (download) (as text)
Tue Sep 13 17:40:57 2016 UTC (7 years, 8 months ago) by z0rac
File MIME type: text/x-c++src
File size: 11154 byte(s)


1 /*
2 * Copyright (C) 2009-2016 TSUBAKIMOTO Hiroya <z0rac@users.sourceforge.jp>
3 *
4 * This software comes with ABSOLUTELY NO WARRANTY; for details of
5 * the license terms, see the LICENSE.txt file included with the program.
6 */
7 #include "define.h"
8 #include "mailbox.h"
9 #include "setting.h"
10 #include "win32.h"
11 #include "window.h"
12 #include <cassert>
13
14 #ifdef _DEBUG
15 #include <iostream>
16 #define DBG(s) s
17 #define LOG(s) (cout << s)
18 #else
19 #define DBG(s)
20 #define LOG(s)
21 #endif
22
23 #ifndef HDF_SORTDOWN
24 #define HDF_SORTDOWN 0x0200
25 #define HDF_SORTUP 0x0400
26 #endif
27
28 /** summary - summary view window
29 */
30 namespace {
31 class summary : public window {
32 class item : LVITEMA {
33 HWND _h;
34 public:
35 item(const window& w);
36 item& operator()(LPCSTR s);
37 item& operator()(LPCWSTR s);
38 item& operator()(const string& s) { return operator()(s.c_str()); }
39 };
40 enum column { SUBJECT, SENDER, DATE, MAILBOX, LAST };
41 time_t* _dates;
42 pair<size_t, string>* _mboxes;
43 int _column;
44 int _order;
45 string _tmp;
46 int _compare(LPARAM s1, LPARAM s2) const;
47 static int CALLBACK compare(LPARAM l1, LPARAM l2, LPARAM lsort);
48 protected:
49 void release();
50 LRESULT notify(WPARAM w, LPARAM l);
51 void _initialize();
52 void _sort(int column, int order);
53 void _open();
54 public:
55 summary(const window& parent);
56 ~summary();
57 int initialize(const mailbox* mboxes);
58 void raised(bool topmost);
59 };
60 }
61
62 summary::summary(const window& parent)
63 : window(WC_LISTVIEW, parent), _dates(NULL), _mboxes(NULL),
64 _column(3), _order(1)
65 {
66 static commctrl listview(ICC_LISTVIEW_CLASSES);
67 style(LVS_REPORT | LVS_SINGLESEL);
68 (void)ListView_SetExtendedListViewStyle
69 (hwnd(), LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_LABELTIP);
70 setting::preferences("summary")["sort"](_column)(_order);
71 show();
72 }
73
74 summary::~summary()
75 {
76 delete [] _dates;
77 delete [] _mboxes;
78 }
79
80 void
81 summary::release()
82 {
83 try {
84 setting prefs = setting::preferences("summary");
85 setting::tuple tuple = setting::tuple(ListView_GetColumnWidth(hwnd(), 0));
86 for (int i = 1; i < LAST; ++i) tuple(ListView_GetColumnWidth(hwnd(), i));
87 if (tuple.row().compare(prefs["columns"])) prefs("columns", tuple);
88 tuple = setting::tuple(_column)(_order);
89 if (tuple.row().compare(prefs["sort"])) prefs("sort", tuple);
90 } catch (...) {}
91 }
92
93 LRESULT
94 summary::notify(WPARAM w, LPARAM l)
95 {
96 switch (LPNMHDR(l)->code) {
97 case LVN_GETDISPINFOA:
98 switch (LPNMLVDISPINFOA(l)->item.iSubItem) {
99 case DATE:
100 {
101 time_t t = _dates[LPNMLVDISPINFOA(l)->item.lParam];
102 _tmp = t == time_t(-1) ? "" :
103 win32::date(t, DATE_LONGDATE) + " " + win32::time(t, TIME_NOSECONDS);
104 LPNMLVDISPINFOA(l)->item.pszText = LPSTR(_tmp.c_str());
105 }
106 break;
107 case MAILBOX:
108 {
109 const pair<size_t, string>* p = _mboxes;
110 while (size_t(LPNMLVDISPINFOA(l)->item.lParam) >= p->first) ++p;
111 LPNMLVDISPINFOA(l)->item.pszText = LPSTR(p->second.c_str());
112 }
113 break;
114 }
115 return 0;
116 case LVN_COLUMNCLICK:
117 _sort(LPNMLISTVIEW(l)->iSubItem,
118 LPNMLISTVIEW(l)->iSubItem == _column ? -_order : 1);
119 return 0;
120 case NM_DBLCLK:
121 _open();
122 return 0;
123 }
124 return window::notify(w, l);
125 }
126
127 void
128 summary::_initialize()
129 {
130 list<string> column = win32::exe.texts(ID_TEXT_SUMMARY_COLUMN);
131 const int n = static_cast<int>(column.size());
132 list<int> width = setting::preferences("summary")["columns"].split<int>();
133 list<int>::iterator wp = width.begin();
134 LVCOLUMN col = { LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM };
135 int cx = 0, ex = extent().x;
136 for (list<string>::iterator p = column.begin(); p != column.end(); ++p) {
137 col.cx = (wp != width.end() ? *wp++ :
138 col.iSubItem ? (ex - cx) / (n - col.iSubItem) : ex / 2);
139 col.pszText = LPSTR(p->c_str());
140 (void)ListView_InsertColumn(hwnd(), col.iSubItem, &col);
141 cx += col.cx;
142 ++col.iSubItem;
143 }
144 }
145
146 void
147 summary::_sort(int column, int order)
148 {
149 HWND hdr = ListView_GetHeader(hwnd());
150 HDITEM hdi = { HDI_FORMAT };
151 if (column != _column) {
152 (void)Header_GetItem(hdr, _column, &hdi);
153 hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
154 (void)Header_SetItem(hdr, _column, &hdi);
155 }
156 (void)Header_GetItem(hdr, column, &hdi);
157 hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
158 hdi.fmt |= order > 0 ? HDF_SORTUP : HDF_SORTDOWN;
159 (void)Header_SetItem(hdr, column, &hdi);
160 _column = column, _order = order;
161 SendMessage(hwnd(), column < DATE ? LVM_SORTITEMSEX : LVM_SORTITEMS,
162 WPARAM(this), LPARAM(&compare));
163 }
164
165 inline int
166 summary::_compare(LPARAM s1, LPARAM s2) const
167 {
168 switch (_column) {
169 case DATE:
170 {
171 size_t v1 = size_t(_dates[s1]);
172 size_t v2 = size_t(_dates[s2]);
173 return v1 < v2 ? -_order : int(v1 != v2) * _order;
174 }
175 case MAILBOX:
176 {
177 size_t v1 = 0, v2 = 0;
178 while (_mboxes[v1].first <= size_t(s1)) ++v1;
179 while (_mboxes[v2].first <= size_t(s2)) ++v2;
180 return v1 < v2 ? -_order : int(v1 != v2) * _order;
181 }
182 }
183
184 win32::textbuf<WCHAR> tb[2];
185 LPARAM si[] = { s1, s2 };
186 for (int i = 0; i < 2; ++i) {
187 LVITEMW lv = { LVIF_TEXT };
188 lv.iSubItem = _column;
189 lv.cchTextMax = 256;
190 LRESULT n;
191 do {
192 lv.pszText = tb[i](lv.cchTextMax <<= 1);
193 n = SendMessage(hwnd(), LVM_GETITEMTEXTW, si[i], LPARAM(&lv));
194 } while (n == lv.cchTextMax - 1);
195 }
196 return lstrcmpiW(tb[0].data, tb[1].data) * _order;
197 }
198
199 int CALLBACK
200 summary::compare(LPARAM l1, LPARAM l2, LPARAM lsort)
201 {
202 return reinterpret_cast<summary*>(lsort)->_compare(l1, l2);
203 }
204
205 void
206 summary::_open()
207 {
208 int item = ListView_GetNextItem(hwnd(), -1, LVNI_SELECTED);
209 if (item < 0) return;
210 LVITEM lv = { LVIF_PARAM, item };
211 if (!ListView_GetItem(hwnd(), &lv)) return;
212 size_t i = 0;
213 while (_mboxes[i].first <= size_t(lv.lParam)) ++i;
214 string mua;
215 setting::mailbox(_mboxes[i].second)["mua"].sep(0)(mua);
216 if (!mua.empty() && win32::shell(mua)) close(true);
217 }
218
219 int
220 summary::initialize(const mailbox* mboxes)
221 {
222 int h = extent().y;
223 _initialize();
224 size_t mails = 0;
225 int mbn = 0;
226 for (const mailbox* mb = mboxes; mb; mb = mb->next()) {
227 mails += mb->mails().size();
228 ++mbn;
229 }
230 assert(!_dates && !_mboxes);
231 _dates = new time_t[mails];
232 _mboxes = new pair<size_t, string>[mbn];
233 mails = 0, mbn = 0;
234 for (const mailbox* mb = mboxes; mb; mb = mb->next()) {
235 LOG("Summary[" << mb->name() << "](" << mb->mails().size() << ")..." << endl);
236 _mboxes[mbn].first = mails + mb->mails().size();
237 _mboxes[mbn++].second = mb->name();
238 list<mail>::const_iterator mp = mb->mails().begin();
239 for (; mp != mb->mails().end(); ++mp) {
240 _dates[mails++] = mp->date();
241 item(*this)
242 (win32::wstr(mp->subject(), CP_UTF8))
243 (win32::wstr(mp->sender(), CP_UTF8))
244 (LPSTR_TEXTCALLBACK)
245 (LPSTR_TEXTCALLBACK);
246 }
247 }
248 _sort(_column, _order);
249 int n = ListView_GetItemCount(hwnd()) - 1;
250 return HIWORD(ListView_ApproximateViewRect(hwnd(), -1, -1, n)) - h;
251 }
252
253 summary::item::item(const window& w)
254 : _h(w.hwnd())
255 {
256 ZeroMemory(this, sizeof(LVITEMA));
257 mask = LVIF_PARAM;
258 lParam = iItem = ListView_GetItemCount(_h);
259 (void)ListView_InsertItem(_h, this);
260 mask = LVIF_TEXT;
261 }
262
263 summary::item&
264 summary::item::operator()(LPCSTR s)
265 {
266 pszText = LPSTR(s);
267 SendMessage(_h, LVM_SETITEMA, 0, LPARAM(this));
268 ++iSubItem;
269 return *this;
270 }
271
272 summary::item&
273 summary::item::operator()(LPCWSTR s)
274 {
275 pszText = LPSTR(s);
276 SendMessage(_h, LVM_SETITEMW, 0, LPARAM(this));
277 ++iSubItem;
278 return *this;
279 }
280
281 void
282 summary::raised(bool topmost)
283 {
284 HWND h = ListView_GetToolTips(hwnd());
285 if (h && ((GetWindowLong(h, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0) != topmost) {
286 SetWindowPos(h, topmost ? HWND_TOPMOST : HWND_NOTOPMOST,
287 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
288 }
289 }
290
291 /** summarywindow - summary popup window
292 */
293 namespace {
294 class summarywindow : public appwindow {
295 summary _summary;
296 bool _changed;
297 struct _autoclose : public window::timer {
298 int sec;
299 void reset(window& source)
300 { if (sec > 0) source.settimer(*this, UINT(sec * 1000)); }
301 void wakeup(window& source)
302 { if (source.hascursor()) reset(source); else source.close(); }
303 } _autoclose;
304 int _alpha;
305 protected:
306 LRESULT dispatch(UINT m, WPARAM w, LPARAM l);
307 void release();
308 void limit(LPMINMAXINFO info);
309 void resize(int w, int h);
310 void raised(bool topmost) { _summary.raised(topmost); }
311 public:
312 summarywindow(const mailbox* mboxes);
313 ~summarywindow() { if (hwnd()) release(); }
314 };
315 }
316
317 summarywindow::summarywindow(const mailbox* mboxes)
318 : _summary(self())
319 {
320 const DWORD style(WS_OVERLAPPED | WS_CAPTION |
321 WS_SYSMENU | WS_THICKFRAME | WS_CLIPCHILDREN);
322 const DWORD exstyle(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE);
323 setting prefs = setting::preferences();
324 int transparency;
325 prefs["summary"](_autoclose.sec = 3)()(transparency = 0);
326 _autoclose.reset(*this);
327 _alpha = 255 - 255 * transparency / 100;
328 if (_alpha < 255) {
329 summarywindow::style(style, exstyle | WS_EX_LAYERED);
330 transparent(_alpha);
331 } else {
332 summarywindow::style(style, exstyle);
333 }
334 SetWindowText(hwnd(), win32::exe.text(ID_TEXT_SUMMARY_TITLE).c_str());
335 RECT r;
336 GetWindowRect(GetDesktopWindow(), &r);
337 LONG top = r.top, bottom = r.bottom;
338 InflateRect(&r, (r.left - r.right) / 4, (r.top - r.bottom) / 4);
339 setting::preferences("summary")
340 ["window"](r.left)(r.top)(r.right)(r.bottom);
341 move(adjust(r));
342 int h = _summary.initialize(mboxes);
343 if (h > 0) {
344 r.bottom += h;
345 if (r.bottom > bottom) {
346 r.top -= r.bottom - bottom, r.bottom = bottom;
347 if (r.top < top) r.top = top;
348 }
349 move(r);
350 }
351 topmost(true);
352 show(true, GetActiveWindow() == GetForegroundWindow());
353 _changed = false;
354 }
355
356 LRESULT
357 summarywindow::dispatch(UINT m, WPARAM w, LPARAM l)
358 {
359 switch (m) {
360 case WM_MOVE:
361 _changed = true;
362 break;
363 case WM_ENDSESSION:
364 if (w) release();
365 break;
366 case WM_CONTEXTMENU:
367 return 0;
368 case WM_NCACTIVATE:
369 if (_alpha < 255) transparent((!LOWORD(w) - 1) | _alpha);
370 break;
371 case WM_NCMOUSEMOVE:
372 _autoclose.reset(*this);
373 break;
374 }
375 return appwindow::dispatch(m, w, l);
376 }
377
378 void
379 summarywindow::release()
380 {
381 if (_changed) {
382 try {
383 RECT r = bounds();
384 setting::preferences("summary")
385 ("window", setting::tuple(r.left)(r.top)(r.right)(r.bottom));
386 } catch (...) {}
387 }
388 }
389
390 void
391 summarywindow::limit(LPMINMAXINFO info)
392 {
393 info->ptMinTrackSize.x = 200;
394 info->ptMinTrackSize.y = 80;
395 }
396
397 void
398 summarywindow::resize(int w, int h)
399 {
400 RECT r = _summary.bounds();
401 _changed = r.right - r.left != w || r.bottom - r.top != h;
402 _summary.move(0, 0, w, h);
403 }
404
405 window*
406 summary(const mailbox* mboxes)
407 {
408 return new summarywindow(mboxes);
409 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26