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