]> cygwin.com Git - cygwin-apps/setup.git/blob - PickView.cc
Make PrereqChecker::setTrust() a static method
[cygwin-apps/setup.git] / PickView.cc
1 /*
2 * Copyright (c) 2002 Robert Collins.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by Robert Collins <robertc@hotmail.com>
13 *
14 */
15
16 #include "PickView.h"
17 #include <algorithm>
18 #include <limits.h>
19 #include <commctrl.h>
20 #include <shlwapi.h>
21 #include "PickPackageLine.h"
22 #include "PickCategoryLine.h"
23 #include "package_db.h"
24 #include "package_version.h"
25 #include "dialog.h"
26 #include "resource.h"
27 /* For 'source' */
28 #include "state.h"
29 #include "LogSingleton.h"
30
31 using namespace std;
32
33 static PickView::Header pkg_headers[] = {
34 {"Current", 0, 0, true},
35 {"New", 0, 0, true},
36 {"Bin?", 0, 0, false},
37 {"Src?", 0, 0, false},
38 {"Categories", 0, 0, true},
39 {"Size", 0, 0, true},
40 {"Package", 0, 0, true},
41 {0, 0, 0, false}
42 };
43
44 static PickView::Header cat_headers[] = {
45 {"Category", 0, 0, true},
46 {"Current", 0, 0, true},
47 {"New", 0, 0, true},
48 {"Bin?", 0, 0, false},
49 {"Src?", 0, 0, false},
50 {"Size", 0, 0, true},
51 {"Package", 0, 0, true},
52 {0, 0, 0, false}
53 };
54
55 ATOM PickView::WindowClassAtom = 0;
56
57 // DoInsertItem - inserts an item into a header control.
58 // Returns the index of the new item.
59 // hwndHeader - handle to the header control.
60 // iInsertAfter - index of the previous item.
61 // nWidth - width of the new item.
62 // lpsz - address of the item string.
63 static int
64 DoInsertItem (HWND hwndHeader, int iInsertAfter, int nWidth, LPSTR lpsz)
65 {
66 HDITEM hdi;
67 int index;
68
69 hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
70 hdi.pszText = lpsz;
71 hdi.cxy = nWidth;
72 hdi.cchTextMax = lstrlen (hdi.pszText);
73 hdi.fmt = HDF_LEFT | HDF_STRING;
74
75 index = SendMessage (hwndHeader, HDM_INSERTITEM,
76 (WPARAM) iInsertAfter, (LPARAM) & hdi);
77
78 return index;
79 }
80
81 int
82 PickView::set_header_column_order (views vm)
83 {
84 if (vm == views::PackageFull || vm == views::PackagePending
85 || vm == views::PackageKeeps || vm == views::PackageSkips
86 || vm == views::PackageUserPicked)
87 {
88 headers = pkg_headers;
89 current_col = 0;
90 new_col = 1;
91 bintick_col = new_col + 1;
92 srctick_col = bintick_col + 1;
93 cat_col = srctick_col + 1;
94 size_col = cat_col + 1;
95 pkg_col = size_col + 1;
96 last_col = pkg_col;
97 }
98 else if (vm == views::Category)
99 {
100 headers = cat_headers;
101 cat_col = 0;
102 current_col = 1;
103 new_col = current_col + 1;
104 bintick_col = new_col + 1;
105 srctick_col = bintick_col + 1;
106 size_col = srctick_col + 1;
107 pkg_col = size_col + 1;
108 last_col = pkg_col;
109 }
110 else
111 return -1;
112 return last_col;
113 }
114
115 void
116 PickView::set_headers ()
117 {
118 if (set_header_column_order (view_mode) == -1)
119 return;
120 while (int n = SendMessage (listheader, HDM_GETITEMCOUNT, 0, 0))
121 {
122 SendMessage (listheader, HDM_DELETEITEM, n - 1, 0);
123 }
124 int i;
125 for (i = 0; i <= last_col; i++)
126 DoInsertItem (listheader, i, headers[i].width, (char *) headers[i].text);
127 }
128
129 void
130 PickView::note_width (PickView::Header *hdrs, HDC dc,
131 const std::string& string, int addend, int column)
132 {
133 SIZE s = { 0, 0 };
134
135 if (string.size())
136 GetTextExtentPoint32 (dc, string.c_str(), string.size(), &s);
137 if (hdrs[column].width < s.cx + addend)
138 hdrs[column].width = s.cx + addend;
139 }
140
141 void
142 PickView::setViewMode (views mode)
143 {
144 view_mode = mode;
145 set_headers ();
146 packagedb db;
147
148 contents.empty ();
149 if (view_mode == PickView::views::Category)
150 {
151 contents.ShowLabel (true);
152 /* start collapsed. TODO: make this a chooser flag */
153 for (packagedb::categoriesType::iterator n =
154 packagedb::categories.begin(); n != packagedb::categories.end();
155 ++n)
156 insert_category (&*n, (*n).first.c_str()[0] == '.'
157 ? CATEGORY_EXPANDED : CATEGORY_COLLAPSED);
158 }
159 else
160 {
161 contents.ShowLabel (false);
162 // iterate through every package
163 for (packagedb::packagecollection::iterator i = db.packages.begin ();
164 i != db.packages.end (); ++i)
165 {
166 packagemeta & pkg = *(i->second);
167
168 if (!pkg.isBinary())
169 continue;
170
171 if ( // "Full" : everything
172 (view_mode == PickView::views::PackageFull)
173
174 // "Pending" : packages that are being added/removed/upgraded
175 || (view_mode == PickView::views::PackagePending &&
176 ((!pkg.desired && pkg.installed) || // uninstall
177 (pkg.desired &&
178 (pkg.desired.picked () || // install bin
179 pkg.desired.sourcePackage ().picked ())))) // src
180
181 // "Up to date" : installed packages that will not be changed
182 || (view_mode == PickView::views::PackageKeeps &&
183 (pkg.installed && pkg.desired && !pkg.desired.picked ()
184 && !pkg.desired.sourcePackage ().picked ()))
185
186 // "Not installed"
187 || (view_mode == PickView::views::PackageSkips &&
188 (!pkg.desired && !pkg.installed))
189
190 // "UserPick" : installed packages that were picked by user
191 || (view_mode == PickView::views::PackageUserPicked &&
192 (pkg.installed && pkg.user_picked)))
193 {
194 // Filter by package name
195 if (packageFilterString.empty ()
196 || StrStrI (pkg.name.c_str (), packageFilterString.c_str ()))
197 insert_pkg (pkg);
198 }
199 }
200 }
201
202 RECT r = GetClientRect ();
203 SCROLLINFO si;
204 memset (&si, 0, sizeof (si));
205 si.cbSize = sizeof (si);
206 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
207 si.nMin = 0;
208 si.nMax = headers[last_col].x + headers[last_col].width; // + HMARGIN;
209 si.nPage = r.right;
210 SetScrollInfo (GetHWND(), SB_HORZ, &si, TRUE);
211
212 si.nMax = contents.itemcount () * row_height;
213 si.nPage = r.bottom - header_height;
214 SetScrollInfo (GetHWND(), SB_VERT, &si, TRUE);
215
216 scroll_ulc_x = scroll_ulc_y = 0;
217
218 InvalidateRect (GetHWND(), &r, TRUE);
219 }
220
221 PickView::views
222 PickView::getViewMode ()
223 {
224 return view_mode;
225 }
226
227 const char *
228 PickView::mode_caption (views mode)
229 {
230 switch (mode)
231 {
232 case views::PackageFull:
233 return "Full";
234 case views::PackagePending:
235 return "Pending";
236 case views::PackageKeeps:
237 return "Up To Date";
238 case views::PackageSkips:
239 return "Not Installed";
240 case views::PackageUserPicked:
241 return "Picked";
242 case views::Category:
243 return "Category";
244 default:
245 return "";
246 }
247 }
248
249 /* meant to be called on packagemeta::categories */
250 bool
251 isObsolete (set <std::string, casecompare_lt_op> &categories)
252 {
253 set <std::string, casecompare_lt_op>::const_iterator i;
254
255 for (i = categories.begin (); i != categories.end (); ++i)
256 if (isObsolete (*i))
257 return true;
258 return false;
259 }
260
261 bool
262 isObsolete (const std::string& catname)
263 {
264 if (casecompare(catname, "ZZZRemovedPackages") == 0
265 || casecompare(catname, "_", 1) == 0)
266 return true;
267 return false;
268 }
269
270 /* Sets the mode for showing/hiding obsolete junk packages. */
271 void
272 PickView::setObsolete (bool doit)
273 {
274 showObsolete = doit;
275 refresh ();
276 }
277
278
279 void
280 PickView::insert_pkg (packagemeta & pkg)
281 {
282 if (!showObsolete && isObsolete (pkg.categories))
283 return;
284
285 PickLine & line = *new PickPackageLine (*this, pkg);
286 contents.insert (line);
287 }
288
289 void
290 PickView::insert_category (Category *cat, bool collapsed)
291 {
292 // Urk, special case
293 if (casecompare(cat->first, "All") == 0 ||
294 (!showObsolete && isObsolete (cat->first)))
295 return;
296 PickCategoryLine & catline = *new PickCategoryLine (*this, *cat, 1, collapsed);
297 int packageCount = 0;
298 for (vector <packagemeta *>::iterator i = cat->second.begin ();
299 i != cat->second.end () ; ++i)
300 {
301 if (packageFilterString.empty () \
302 || (*i
303 && StrStrI ((*i)->name.c_str (), packageFilterString.c_str ())))
304 {
305 PickLine & line = *new PickPackageLine (*this, **i);
306 catline.insert (line);
307 packageCount++;
308 }
309 }
310
311 if (packageFilterString.empty () || packageCount)
312 contents.insert (catline);
313 else
314 delete &catline;
315 }
316
317 int
318 PickView::click (int row, int x)
319 {
320 return contents.click (0, row, x);
321 }
322
323
324 void
325 PickView::scroll (HWND hwnd, int which, int *var, int code, int howmany = 1)
326 {
327 SCROLLINFO si;
328 si.cbSize = sizeof (si);
329 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
330 GetScrollInfo (hwnd, which, &si);
331
332 switch (code)
333 {
334 case SB_THUMBTRACK:
335 si.nPos = si.nTrackPos;
336 break;
337 case SB_THUMBPOSITION:
338 break;
339 case SB_BOTTOM:
340 si.nPos = si.nMax;
341 break;
342 case SB_TOP:
343 si.nPos = 0;
344 break;
345 case SB_LINEDOWN:
346 si.nPos += (row_height * howmany);
347 break;
348 case SB_LINEUP:
349 si.nPos -= (row_height * howmany);
350 break;
351 case SB_PAGEDOWN:
352 si.nPos += si.nPage * 9 / 10;
353 break;
354 case SB_PAGEUP:
355 si.nPos -= si.nPage * 9 / 10;
356 break;
357 }
358
359 if ((int) si.nPos < 0)
360 si.nPos = 0;
361 if (si.nPos + si.nPage > (unsigned int) si.nMax)
362 si.nPos = si.nMax - si.nPage;
363
364 si.fMask = SIF_POS;
365 SetScrollInfo (hwnd, which, &si, TRUE);
366
367 int ox = scroll_ulc_x;
368 int oy = scroll_ulc_y;
369 *var = si.nPos;
370
371 RECT cr, sr;
372 ::GetClientRect (hwnd, &cr);
373 sr = cr;
374 sr.top += header_height;
375 UpdateWindow (hwnd);
376 ScrollWindow (hwnd, ox - scroll_ulc_x, oy - scroll_ulc_y, &sr, &sr);
377 /*
378 sr.bottom = sr.top;
379 sr.top = cr.top;
380 ScrollWindow (hwnd, ox - scroll_ulc_x, 0, &sr, &sr);
381 */
382 if (ox - scroll_ulc_x)
383 {
384 ::GetClientRect (listheader, &cr);
385 sr = cr;
386 // UpdateWindow (htmp);
387 ::MoveWindow (listheader, -scroll_ulc_x, 0,
388 headers[last_col].x +
389 headers[last_col].width, header_height, TRUE);
390 }
391 UpdateWindow (hwnd);
392 }
393
394 /* this means to make the 'category' column wide enough to fit the first 'n'
395 categories for each package. */
396 #define NUM_CATEGORY_COL_WIDTH 2
397
398 void
399 PickView::init_headers (HDC dc)
400 {
401 int i;
402
403 for (i = 0; headers[i].text; i++)
404 {
405 headers[i].width = 0;
406 headers[i].x = 0;
407 }
408
409 // A margin of 3*GetSystemMetrics(SM_CXEDGE) is used at each side of the
410 // header text. (Probably should use that rather than hard-coding HMARGIN
411 // everywhere)
412 int addend = 2*3*GetSystemMetrics(SM_CXEDGE);
413
414 // accommodate widths of the 'bin' and 'src' checkbox columns
415 note_width (headers, dc, headers[bintick_col].text, addend, bintick_col);
416 note_width (headers, dc, headers[srctick_col].text, addend, srctick_col);
417
418 // accomodate the width of each category name
419 packagedb db;
420 for (packagedb::categoriesType::iterator n = packagedb::categories.begin();
421 n != packagedb::categories.end(); ++n)
422 {
423 if (!showObsolete && isObsolete (n->first))
424 continue;
425 note_width (headers, dc, n->first, HMARGIN, cat_col);
426 }
427
428 /* For each package, accomodate the width of the installed version in the
429 current_col, the widths of all other versions in the new_col, and the
430 width of the sdesc for the pkg_col. Also, if this is not a Category
431 view, adjust the 'category' column so that the first NUM_CATEGORY_COL_WIDTH
432 categories from each package fits. */
433 for (packagedb::packagecollection::iterator n = db.packages.begin ();
434 n != db.packages.end (); ++n)
435 {
436 packagemeta & pkg = *(n->second);
437 if (!showObsolete && isObsolete (pkg.categories))
438 continue;
439 if (pkg.installed)
440 note_width (headers, dc, pkg.installed.Canonical_version (),
441 HMARGIN, current_col);
442 for (set<packageversion>::iterator i = pkg.versions.begin ();
443 i != pkg.versions.end (); ++i)
444 {
445 if (*i != pkg.installed)
446 note_width (headers, dc, i->Canonical_version (),
447 HMARGIN + SPIN_WIDTH, new_col);
448 std::string z = format_1000s(i->source ()->size);
449 note_width (headers, dc, z, HMARGIN, size_col);
450 z = format_1000s(i->sourcePackage ().source ()->size);
451 note_width (headers, dc, z, HMARGIN, size_col);
452 }
453 std::string s = pkg.name;
454 if (pkg.SDesc ().size())
455 s += std::string (": ") + std::string(pkg.SDesc ());
456 note_width (headers, dc, s, HMARGIN, pkg_col);
457
458 if (view_mode != PickView::views::Category && pkg.categories.size () > 2)
459 {
460 std::string compound_cat("");
461 std::set<std::string, casecompare_lt_op>::const_iterator cat;
462 size_t cnt;
463
464 for (cnt = 0, cat = pkg.categories.begin ();
465 cnt < NUM_CATEGORY_COL_WIDTH && cat != pkg.categories.end ();
466 ++cat)
467 {
468 if (casecompare(*cat, "All") == 0)
469 continue;
470 if (compound_cat.size ())
471 compound_cat += ", ";
472 compound_cat += *cat;
473 cnt++;
474 }
475 note_width (headers, dc, compound_cat, HMARGIN, cat_col);
476 }
477 }
478
479 // ensure that the new_col is wide enough for all the labels
480 const char *captions[] = { "Uninstall", "Skip", "Reinstall", "Retrieve",
481 "Source", "Keep", NULL };
482 for (int i = 0; captions[i]; i++)
483 note_width (headers, dc, captions[i], HMARGIN + SPIN_WIDTH, new_col);
484
485 // finally, compute the actual x values based on widths
486 headers[0].x = 0;
487 for (i = 1; i <= last_col; i++)
488 headers[i].x = headers[i - 1].x + headers[i - 1].width;
489 // and allow for resizing to ensure the last column reaches
490 // all the way to the end of the chooser box.
491 headers[last_col].width += total_delta_x;
492 }
493
494 PickView::PickView (Category &cat) : deftrust (TRUST_CURR),
495 contents (*this, cat, 0, false, true), showObsolete (false),
496 packageFilterString (), hasWindowRect (false), total_delta_x (0)
497 {
498 }
499
500 void
501 PickView::init(views _mode)
502 {
503 HDC dc = GetDC (GetHWND());
504 sysfont = GetStockObject (DEFAULT_GUI_FONT);
505 SelectObject (dc, sysfont);
506 GetTextMetrics (dc, &tm);
507
508 bitmap_dc = CreateCompatibleDC (dc);
509 #define LI(x) LoadImage (hinstance, MAKEINTRESOURCE (x), IMAGE_BITMAP, 0, 0, 0);
510 bm_spin = LI (IDB_SPIN);
511 bm_checkyes = LI (IDB_CHECK_YES);
512 bm_checkno = LI (IDB_CHECK_NO);
513 bm_checkna = LI (IDB_CHECK_NA);
514 bm_treeplus = LI (IDB_TREE_PLUS);
515 bm_treeminus = LI (IDB_TREE_MINUS);
516 #undef LI
517 icon_dc = CreateCompatibleDC (dc);
518 bm_icon = CreateCompatibleBitmap (dc, 11, 11);
519 SelectObject (icon_dc, bm_icon);
520 rect_icon = CreateRectRgn (0, 0, 11, 11);
521
522 row_height = (tm.tmHeight + tm.tmExternalLeading + ROW_MARGIN);
523 int irh = tm.tmExternalLeading + tm.tmDescent + 11 + ROW_MARGIN;
524 if (row_height < irh)
525 row_height = irh;
526
527 HDLAYOUT hdl;
528 WINDOWPOS wp;
529
530 // Ensure that the common control DLL is loaded, and then create
531 // the header control.
532 INITCOMMONCONTROLSEX controlinfo = { sizeof (INITCOMMONCONTROLSEX),
533 ICC_LISTVIEW_CLASSES };
534 InitCommonControlsEx (&controlinfo);
535
536 if ((listheader = CreateWindowEx (0, WC_HEADER, (LPCTSTR) NULL,
537 WS_CHILD | WS_BORDER | CCS_NORESIZE |
538 // | HDS_BUTTONS
539 HDS_HORZ, 0, 0, 0, 0, GetHWND(),
540 (HMENU) IDC_CHOOSE_LISTHEADER, hinstance,
541 (LPVOID) NULL)) == NULL)
542 // FIXME: throw an exception
543 exit (10);
544
545 // Retrieve the bounding rectangle of the parent window's
546 // client area, and then request size and position values
547 // from the header control.
548 RECT rcParent = GetClientRect ();
549
550 hdl.prc = &rcParent;
551 hdl.pwpos = &wp;
552 if (!SendMessage (listheader, HDM_LAYOUT, 0, (LPARAM) & hdl))
553 // FIXME: throw an exception
554 exit (11);
555
556 // Set the font of the listheader, but don't redraw, because its not shown
557 // yet.This message does not return a value, so we are not checking it as we
558 // do above.
559 SendMessage (listheader, WM_SETFONT, (WPARAM) sysfont, FALSE);
560
561 // Set the size, position, and visibility of the header control.
562 SetWindowPos (listheader, wp.hwndInsertAfter, wp.x, wp.y,
563 wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW);
564
565 header_height = wp.cy;
566 ReleaseDC (GetHWND (), dc);
567
568 view_mode = _mode;
569 refresh ();
570 }
571
572 PickView::~PickView()
573 {
574 DeleteDC (bitmap_dc);
575 DeleteObject (bm_spin);
576 DeleteObject (bm_checkyes);
577 DeleteObject (bm_checkno);
578 DeleteObject (bm_checkna);
579 DeleteObject (bm_treeplus);
580 DeleteObject (bm_treeminus);
581 DeleteObject (rect_icon);
582 DeleteObject (bm_icon);
583 DeleteDC (icon_dc);
584 }
585
586 bool PickView::registerWindowClass ()
587 {
588 if (WindowClassAtom != 0)
589 return true;
590
591 // We're not registered yet
592 WNDCLASSEX wc;
593
594 wc.cbSize = sizeof (wc);
595 // Some sensible style defaults
596 wc.style = CS_HREDRAW | CS_VREDRAW;
597 // Our default window procedure. This replaces itself
598 // on the first call with the simpler Window::WindowProcReflector().
599 wc.lpfnWndProc = Window::FirstWindowProcReflector;
600 // No class bytes
601 wc.cbClsExtra = 0;
602 // One pointer to REFLECTION_INFO in the extra window instance bytes
603 wc.cbWndExtra = 4;
604 // The app instance
605 wc.hInstance = hinstance; //GetInstance ();
606 // Use a bunch of system defaults for the GUI elements
607 wc.hIcon = LoadIcon (0, IDI_APPLICATION);
608 wc.hIconSm = NULL;
609 wc.hCursor = LoadCursor (0, IDC_ARROW);
610 wc.hbrBackground = NULL;
611 // No menu
612 wc.lpszMenuName = NULL;
613 // We'll get a little crazy here with the class name
614 wc.lpszClassName = "listview";
615
616 // All set, try to register
617 WindowClassAtom = RegisterClassEx (&wc);
618 if (WindowClassAtom == 0)
619 Log (LOG_BABBLE) << "Failed to register listview " << GetLastError () << endLog;
620 return WindowClassAtom != 0;
621 }
622
623 LRESULT CALLBACK
624 PickView::list_vscroll (HWND hwnd, HWND hctl, UINT code, int pos)
625 {
626 scroll (hwnd, SB_VERT, &scroll_ulc_y, code);
627 return 0;
628 }
629
630 LRESULT CALLBACK
631 PickView::list_hscroll (HWND hwnd, HWND hctl, UINT code, int pos)
632 {
633 scroll (hwnd, SB_HORZ, &scroll_ulc_x, code);
634 return 0;
635 }
636
637 void
638 PickView::set_vscroll_info (const RECT &r)
639 {
640 SCROLLINFO si;
641 memset (&si, 0, sizeof (si));
642 si.cbSize = sizeof (si);
643 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; /* SIF_RANGE was giving strange behaviour */
644 si.nMin = 0;
645
646 si.nMax = contents.itemcount () * row_height;
647 si.nPage = r.bottom - header_height;
648
649 /* if we are under the minimum display count ,
650 * set the offset to 0
651 */
652 if ((unsigned int) si.nMax <= si.nPage)
653 scroll_ulc_y = 0;
654 si.nPos = scroll_ulc_y;
655
656 SetScrollInfo (GetHWND(), SB_VERT, &si, TRUE);
657 }
658
659 LRESULT CALLBACK
660 PickView::list_click (HWND hwnd, BOOL dblclk, int x, int y, UINT hitCode)
661 {
662 int row, refresh __attribute__ ((unused));
663
664 if (contents.itemcount () == 0)
665 return 0;
666
667 if (y < header_height)
668 return 0;
669 x += scroll_ulc_x;
670 y += scroll_ulc_y - header_height;
671
672 row = (y + ROW_MARGIN / 2) / row_height;
673
674 if (row < 0 || row >= contents.itemcount ())
675 return 0;
676
677 refresh = click (row, x);
678
679 // XXX we need a method to query the database to see if more
680 // than just one package has changed! Until then...
681 #if 0
682 if (refresh)
683 {
684 #endif
685 RECT r = GetClientRect ();
686 set_vscroll_info (r);
687 InvalidateRect (GetHWND(), &r, TRUE);
688 #if 0
689 }
690 else
691 {
692 RECT rect;
693 rect.left =
694 headers[new_col].x - scroll_ulc_x;
695 rect.right =
696 headers[src_col + 1].x - scroll_ulc_x;
697 rect.top =
698 header_height + row * row_height -
699 scroll_ulc_y;
700 rect.bottom = rect.top + row_height;
701 InvalidateRect (hwnd, &rect, TRUE);
702 }
703 #endif
704 return 0;
705 }
706
707 /*
708 * LRESULT CALLBACK
709 * PickView::listview_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
710 */
711 LRESULT
712 PickView::WindowProc (UINT message, WPARAM wParam, LPARAM lParam)
713 {
714 int wheel_notches;
715 UINT wheel_lines;
716
717 switch (message)
718 {
719 case WM_HSCROLL:
720 list_hscroll (GetHWND(), (HWND)lParam, LOWORD(wParam), HIWORD(wParam));
721 return 0;
722 case WM_VSCROLL:
723 list_vscroll (GetHWND(), (HWND)lParam, LOWORD(wParam), HIWORD(wParam));
724 return 0;
725 case WM_MOUSEWHEEL:
726 // this is how many 'notches' the wheel scrolled, forward/up = positive
727 wheel_notches = GET_WHEEL_DELTA_WPARAM(wParam) / 120;
728
729 // determine how many lines the user has configred for a mouse scroll
730 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_lines, 0);
731
732 if (wheel_lines == 0) // do no scrolling
733 return 0;
734 else if (wheel_lines == WHEEL_PAGESCROLL)
735 scroll (GetHWND (), SB_VERT, &scroll_ulc_y, (wheel_notches > 0) ?
736 SB_PAGEUP : SB_PAGEDOWN);
737 else
738 scroll (GetHWND (), SB_VERT, &scroll_ulc_y, (wheel_notches > 0) ?
739 SB_LINEUP : SB_LINEDOWN, wheel_lines * abs (wheel_notches));
740 return 0; // handled
741 case WM_LBUTTONDOWN:
742 list_click (GetHWND(), FALSE, LOWORD(lParam), HIWORD(lParam), wParam);
743 return 0;
744 case WM_PAINT:
745 paint (GetHWND());
746 return 0;
747 case WM_NOTIFY:
748 {
749 // pnmh = (LPNMHDR) lParam
750 LPNMHEADER phdr = (LPNMHEADER) lParam;
751 switch (phdr->hdr.code)
752 {
753 case HDN_ITEMCHANGED:
754 if (phdr->hdr.hwndFrom == ListHeader ())
755 {
756 if (phdr->pitem && phdr->pitem->mask & HDI_WIDTH)
757 headers[phdr->iItem].width = phdr->pitem->cxy;
758
759 for (int i = 1; i <= last_col; i++)
760 headers[i].x = headers[i - 1].x + headers[i - 1].width;
761
762 RECT r = GetClientRect ();
763 SCROLLINFO si;
764 si.cbSize = sizeof (si);
765 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
766 GetScrollInfo (GetHWND(), SB_HORZ, &si);
767
768 int oldMax = si.nMax;
769 si.nMax = headers[last_col].x + headers[last_col].width;
770 if (si.nTrackPos && oldMax > si.nMax)
771 si.nTrackPos += si.nMax - oldMax;
772
773 si.nPage = r.right;
774 SetScrollInfo (GetHWND(), SB_HORZ, &si, TRUE);
775 InvalidateRect (GetHWND(), &r, TRUE);
776 if (si.nTrackPos && oldMax > si.nMax)
777 scroll (GetHWND(), SB_HORZ, &scroll_ulc_x, SB_THUMBTRACK);
778 }
779 break;
780 }
781 }
782 break;
783 case WM_SIZE:
784 {
785 // Note: WM_SIZE msgs only appear when 'just' scrolling the window
786 RECT windowRect = GetWindowRect ();
787 if (hasWindowRect)
788 {
789 int dx;
790 if ((dx = windowRect.right - windowRect.left -
791 lastWindowRect.width ()) != 0)
792 {
793 cat_headers[set_header_column_order (views::Category)].width += dx;
794 pkg_headers[set_header_column_order (views::PackagePending)].width += dx;
795 set_header_column_order (view_mode);
796 set_headers ();
797 ::MoveWindow (listheader, -scroll_ulc_x, 0,
798 headers[last_col].x +
799 headers[last_col].width, header_height, TRUE);
800 total_delta_x += dx;
801 }
802 if (windowRect.bottom - windowRect.top - lastWindowRect.height ())
803 set_vscroll_info (GetClientRect ());
804 }
805 else
806 hasWindowRect = true;
807
808 lastWindowRect = windowRect;
809 return 0;
810 }
811 }
812
813 // default: can't handle this message
814 return DefWindowProc (GetHWND(), message, wParam, lParam);
815 }
816
817 ////
818 // Turn black into foreground color and white into background color by
819 // 1) Filling a square with ~(FG^BG)
820 // 2) Blitting the bitmap on it with NOTSRCERASE (white->black; black->FG^BG)
821 // 3) Blitting the result on BG with SRCINVERT (white->BG; black->FG)
822 void
823 PickView::DrawIcon (HDC hdc, int x, int y, HANDLE hIcon)
824 {
825 SelectObject (bitmap_dc, hIcon);
826 FillRgn (icon_dc, rect_icon, bg_fg_brush);
827 BitBlt (icon_dc, 0, 0, 11, 11, bitmap_dc, 0, 0, NOTSRCERASE);
828 BitBlt (hdc, x, y, 11, 11, icon_dc, 0, 0, SRCINVERT);
829 ///////////// On WinNT-based systems, we could've done the below instead
830 ///////////// See http://support.microsoft.com/default.aspx?scid=kb;en-us;79212
831 // SelectObject (hdc, GetSysColorBrush (COLOR_WINDOWTEXT));
832 // HBITMAP bm_icon = CreateBitmap (11, 11, 1, 1, NULL);
833 // SelectObject (icon_dc, bm_icon);
834 // BitBlt (icon_dc, 0, 0, 11, 11, bitmap_dc, 0, 0, SRCCOPY);
835 // MaskBlt (hdc, x2, by, 11, 11, bitmap_dc, 0, 0, bm_icon, 0, 0, MAKEROP4 (SRCAND, PATCOPY));
836 // DeleteObject (bm_icon);
837 }
838
839 void
840 PickView::paint (HWND hwnd)
841 {
842 // we want to retrieve the update region before calling BeginPaint,
843 // because after we do that the update region is validated and we can
844 // no longer retrieve it
845 HRGN hUpdRgn = CreateRectRgn (0, 0, 0, 0);
846
847 if (GetUpdateRgn (hwnd, hUpdRgn, FALSE) == 0)
848 {
849 // error?
850 return;
851 }
852
853 // tell the system that we're going to begin painting our window
854 // it will prevent further WM_PAINT messages from arriving until we're
855 // done, and if any part of our window was invalidated while we are
856 // painting, it will retrigger us so that we can fix it
857 PAINTSTRUCT ps;
858 HDC hdc = BeginPaint (hwnd, &ps);
859
860 SelectObject (hdc, sysfont);
861 SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT));
862 SetBkColor (hdc, GetSysColor (COLOR_WINDOW));
863 FillRgn (hdc, hUpdRgn, GetSysColorBrush(COLOR_WINDOW));
864
865 COLORREF clr = ~GetSysColor (COLOR_WINDOW) ^ GetSysColor (COLOR_WINDOWTEXT);
866 clr = RGB (GetRValue (clr), GetGValue (clr), GetBValue (clr)); // reconvert
867 bg_fg_brush = CreateSolidBrush (clr);
868
869 RECT cr;
870 ::GetClientRect (hwnd, &cr);
871
872 int x = cr.left - scroll_ulc_x;
873 int y = cr.top - scroll_ulc_y + header_height;
874
875 contents.paint (hdc, hUpdRgn, x, y, 0, (view_mode ==
876 PickView::views::Category) ? 0 : 1);
877
878 if (contents.itemcount () == 0)
879 {
880 static const char *msg = "Nothing to Install/Update";
881 if (source == IDC_SOURCE_DOWNLOAD)
882 msg = "Nothing to Download";
883 TextOut (hdc, x + HMARGIN, y, msg, strlen (msg));
884 }
885
886 DeleteObject (hUpdRgn);
887 DeleteObject (bg_fg_brush);
888 EndPaint (hwnd, &ps);
889 }
890
891
892 bool
893 PickView::Create (Window * parent, DWORD Style, RECT *r)
894 {
895
896 // First register the window class, if we haven't already
897 if (!registerWindowClass ())
898 {
899 // Registration failed
900 return false;
901 }
902
903 // Save our parent, we'll probably need it eventually.
904 setParent(parent);
905
906 // Create the window instance
907 CreateWindowEx (// Extended Style
908 WS_EX_CLIENTEDGE,
909 // window class atom (name)
910 "listview", //MAKEINTATOM(WindowClassAtom),
911 "listviewwindow", // no title-bar string yet
912 // Style bits
913 Style,
914 r ? r->left : CW_USEDEFAULT,
915 r ? r->top : CW_USEDEFAULT,
916 r ? r->right - r->left + 1 : CW_USEDEFAULT,
917 r ? r->bottom - r->top + 1 : CW_USEDEFAULT,
918 // Parent Window
919 parent == NULL ? (HWND)NULL : parent->GetHWND (),
920 // use class menu
921 (HMENU) MAKEINTRESOURCE (IDC_CHOOSE_LIST),
922 // The application instance
923 GetInstance (),
924 // The this ptr, which we'll use to set up
925 // the WindowProc reflection.
926 reinterpret_cast<void *>((Window *)this));
927 if (GetHWND() == NULL)
928 {
929 Log (LOG_BABBLE) << "Failed to create PickView " << GetLastError () << endLog;
930 return false;
931 }
932
933 return true;
934 }
935
936 void
937 PickView::defaultTrust (trusts trust)
938 {
939 this->deftrust = trust;
940
941 packagedb db;
942 db.defaultTrust(trust);
943
944 // force the picker to redraw
945 RECT r = GetClientRect ();
946 InvalidateRect (this->GetHWND(), &r, TRUE);
947 }
948
949 /* This recalculates all column widths and resets the view */
950 void
951 PickView::refresh()
952 {
953 HDC dc = GetDC (GetHWND ());
954
955 // we must set the font of the DC here, otherwise the width calculations
956 // will be off because the system will use the wrong font metrics
957 sysfont = GetStockObject (DEFAULT_GUI_FONT);
958 SelectObject (dc, sysfont);
959
960 // init headers for the current mode
961 set_headers ();
962 init_headers (dc);
963
964 // save the current mode
965 views cur_view_mode = view_mode;
966
967 // switch to the other type and do those headers
968 view_mode = (view_mode == PickView::views::Category) ?
969 PickView::views::PackageFull : PickView::views::Category;
970 set_headers ();
971 init_headers (dc);
972 ReleaseDC (GetHWND (), dc);
973
974 view_mode = cur_view_mode;
975 setViewMode (view_mode);
976 }
This page took 0.074402 seconds and 5 git commands to generate.