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