Each line is implemented by an object of a subclass of ListViewLine, either PickPackageLine, or (in catgeory view) PickCategoryLine
v2: Defer constructing category tree until packagedb is available Also handle an empty category tree Remove unused bitmaps Scoping of strings returned to LVN_GETDISPINFO Avoid doubled 'all' category Update some number of lines, rather than just the current one v3: Speed up calculating column widths, by caching the DC used Don't bother recalculating column widths when obsolete packages are shown/hidden, just allow for obsolete packages always (which makes hardly any difference) Don't bother calculating column widths separately for category view since they are now always the same. v4: Store current category action in CategoryTree Recurse category action onto packages This slightly changes the behaviour: previously, a category action only effected packages which matched the name search filter. Now all packages in contained by the category are effected. v5: When updating, turn off redraw before emptying listview Make headers a parameter to init Don't leak contents Only resize columns once Remove ListView OnMessage as it has nothing to do Never use a column width less than minimum Use LVS_SINGLESEL Factor out string cache for re-use Preserve focused row over ListvIew::setContents() --- ListView.cc | 308 +++++++++++++++ ListView.h | 73 ++++ Makefile.am | 11 +- PickCategoryLine.cc | 135 +------ PickCategoryLine.h | 73 +--- PickLine.h | 47 --- PickPackageLine.cc | 127 +++---- PickPackageLine.h | 25 +- PickView.cc | 888 ++++++++------------------------------------ PickView.h | 217 ++++++----- check-na.bmp | Bin 106 -> 0 bytes check-no.bmp | Bin 106 -> 0 bytes check-yes.bmp | Bin 106 -> 0 bytes choose-spin.bmp | Bin 106 -> 0 bytes choose.cc | 71 ++-- choose.h | 6 +- main.cc | 2 +- res.rc | 17 +- resource.h | 11 - tree-minus.bmp | Bin 106 -> 0 bytes tree-plus.bmp | Bin 106 -> 0 bytes 21 files changed, 799 insertions(+), 1212 deletions(-) create mode 100644 ListView.cc create mode 100644 ListView.h delete mode 100644 PickLine.h delete mode 100644 check-na.bmp delete mode 100644 check-no.bmp delete mode 100644 check-yes.bmp delete mode 100644 choose-spin.bmp delete mode 100644 tree-minus.bmp delete mode 100644 tree-plus.bmp diff --git a/ListView.cc b/ListView.cc new file mode 100644 index 0000000..97ee44c --- /dev/null +++ b/ListView.cc @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2016 Jon Turney + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + */ + +#include "ListView.h" +#include "LogSingleton.h" + +#include <commctrl.h> + +// --------------------------------------------------------------------------- +// implements class ListView +// +// ListView Common Control +// --------------------------------------------------------------------------- + +void +ListView::init(HWND parent, int id, HeaderList headers) +{ + hWndParent = parent; + + // locate the listview control + hWndListView = ::GetDlgItem(parent, id); + + // configure the listview control + SendMessage(hWndListView, CCM_SETVERSION, 6, 0); + + ListView_SetExtendedListViewStyle(hWndListView, + LVS_EX_COLUMNSNAPPOINTS | // use cxMin + LVS_EX_FULLROWSELECT | + LVS_EX_GRIDLINES | + LVS_EX_HEADERDRAGDROP); // headers can be re-ordered + + // give the header control a border + HWND hWndHeader = ListView_GetHeader(hWndListView); + SetWindowLongPtr(hWndHeader, GWL_STYLE, + GetWindowLongPtr(hWndHeader, GWL_STYLE) | WS_BORDER); + + // ensure an initial item exists for width calculations... + LVITEM lvi; + lvi.mask = LVIF_TEXT; + lvi.iItem = 0; + lvi.iSubItem = 0; + lvi.pszText = const_cast <char *> ("Working..."); + ListView_InsertItem(hWndListView, &lvi); + + // populate with columns + initColumns(headers); +} + +void +ListView::initColumns(HeaderList headers_) +{ + // store HeaderList for later use + headers = headers_; + + // create the columns + LVCOLUMN lvc; + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + + int i; + for (i = 0; headers[i].text != 0; i++) + { + lvc.iSubItem = i; + lvc.pszText = const_cast <char *> (headers[i].text); + lvc.cx = 100; + lvc.fmt = headers[i].fmt; + + ListView_InsertColumn(hWndListView, i, &lvc); + } + + // now do some width calculations + for (i = 0; headers[i].text != 0; i++) + { + headers[i].width = 0; + + ListView_SetColumnWidth(hWndListView, i, LVSCW_AUTOSIZE_USEHEADER); + headers[i].hdr_width = ListView_GetColumnWidth(hWndListView, i); + } +} + +void +ListView::noteColumnWidthStart() +{ + dc = GetDC (hWndListView); + + // we must set the font of the DC here, otherwise the width calculations + // will be off because the system will use the wrong font metrics + HANDLE sysfont = GetStockObject (DEFAULT_GUI_FONT); + SelectObject (dc, sysfont); + + int i; + for (i = 0; headers[i].text != 0; i++) + { + headers[i].width = 0; + } +} + +void +ListView::noteColumnWidth(int col_num, const std::string& string) +{ + SIZE s = { 0, 0 }; + + // A margin of 3*GetSystemMetrics(SM_CXEDGE) is used at each side of the + // header text. + int addend = 2*3*GetSystemMetrics(SM_CXEDGE); + + if (string.size()) + GetTextExtentPoint32 (dc, string.c_str(), string.size(), &s); + + int width = addend + s.cx; + + if (width > headers[col_num].width) + headers[col_num].width = width; +} + +void +ListView::noteColumnWidthEnd() +{ + ReleaseDC(hWndListView, dc); +} + +void +ListView::resizeColumns(void) +{ + // ensure the last column stretches all the way to the right-hand side of the + // listview control + int i; + int total = 0; + for (i = 0; headers[i].text != 0; i++) + total = total + headers[i].width; + + RECT r; + GetClientRect(hWndListView, &r); + int width = r.right - r.left; + + if (total < width) + headers[i-1].width += width - total; + + // size each column + LVCOLUMN lvc; + lvc.mask = LVCF_WIDTH | LVCF_MINWIDTH; + for (i = 0; headers[i].text != 0; i++) + { + lvc.iSubItem = i; + lvc.cx = (headers[i].width < headers[i].hdr_width) ? headers[i].hdr_width : headers[i].width; + lvc.cxMin = headers[i].hdr_width; +#if DEBUG + Log (LOG_BABBLE) << "resizeColumns: " << i << " cx " << lvc.cx << " cxMin " << lvc.cxMin <<endLog; +#endif + + ListView_SetColumn(hWndListView, i, &lvc); + } +} + +void +ListView::setContents(ListViewContents *_contents) +{ + contents = _contents; + + // disable redrawing of ListView + // (otherwise it will redraw every time a row is added, which makes this very slow) + SendMessage(hWndListView, WM_SETREDRAW, FALSE, 0); + + // preserve focus/selection + int iRow = ListView_GetSelectionMark(hWndListView); + + empty(); + + size_t i; + for (i = 0; i < contents->size(); i++) + { + LVITEM lvi; + lvi.mask = LVIF_TEXT; + lvi.iItem = i; + lvi.iSubItem = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; + + ListView_InsertItem(hWndListView, &lvi); + } + + if (iRow >= 0) + { + ListView_SetItemState(hWndListView, iRow, LVNI_SELECTED | LVNI_FOCUSED, LVNI_SELECTED | LVNI_FOCUSED); + ListView_EnsureVisible(hWndListView, iRow, false); + } + + // enable redrawing of ListView and redraw + SendMessage(hWndListView, WM_SETREDRAW, TRUE, 0); + RedrawWindow(hWndListView, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); +} + +// Helper class: The char * pointer we hand back needs to remain valid for some +// time after OnNotify returns, when the std::string we have retrieved has gone +// out of scope, so a static instance of this class maintains a local cache. +class StringCache +{ +public: + StringCache() : cache(NULL), cache_size(0) { } + StringCache & operator = (const std::string & s) + { + if ((s.length() + 1) > cache_size) + { + cache_size = s.length() + 1; + cache = (char *)realloc(cache, cache_size); + } + strcpy(cache, s.c_str()); + return *this; + } + operator char *() const + { + return cache; + } +private: + char *cache; + size_t cache_size; +}; + +bool +ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult) +{ +#if DEBUG + Log (LOG_BABBLE) << "ListView::OnNotify id:" << pNmHdr->idFrom << " hwnd:" << pNmHdr->hwndFrom << " code:" << (int)pNmHdr->code << endLog; +#endif + + switch (pNmHdr->code) + { + case LVN_GETDISPINFO: + { + NMLVDISPINFO *pNmLvDispInfo = (NMLVDISPINFO *)pNmHdr; +#if DEBUG + Log (LOG_BABBLE) << "LVN_GETDISPINFO " << pNmLvDispInfo->item.iItem << endLog; +#endif + if (contents) + { + int iRow = pNmLvDispInfo->item.iItem; + int iCol = pNmLvDispInfo->item.iSubItem; + + static StringCache s; + s = (*contents)[iRow]->get_text(iCol); + pNmLvDispInfo->item.pszText = s; + } + + return true; + } + break; + + case LVN_GETEMPTYMARKUP: + { + NMLVEMPTYMARKUP *pNmMarkup = (NMLVEMPTYMARKUP*) pNmHdr; + + MultiByteToWideChar(CP_UTF8, 0, + empty_list_text, -1, + pNmMarkup->szMarkup, L_MAX_URL_LENGTH); + + *pResult = true; + return true; + } + break; + + case NM_CLICK: + { + NMITEMACTIVATE *pNmItemAct = (NMITEMACTIVATE *) pNmHdr; +#if DEBUG + Log (LOG_BABBLE) << "NM_CLICK: pnmitem->iItem " << pNmItemAct->iItem << " pNmItemAct->iSubItem " << pNmItemAct->iSubItem << endLog; +#endif + int iRow = pNmItemAct->iItem; + int iCol = pNmItemAct->iSubItem; + + if (iRow >= 0) + { + // Inform the item of the click + int update = (*contents)[iRow]->do_action(iCol); + + // Update items, if needed + if (update > 0) + { + ListView_RedrawItems(hWndListView, iRow, iRow + update -1); + } + } + return true; + } + break; + } + + // We don't care. + return false; +} + +void +ListView::empty(void) +{ + ListView_DeleteAllItems(hWndListView); +} + +void +ListView::setEmptyText(const char *text) +{ + empty_list_text = text; +} diff --git a/ListView.h b/ListView.h new file mode 100644 index 0000000..d339011 --- /dev/null +++ b/ListView.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Jon Turney + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + */ + +#ifndef SETUP_LISTVIEW_H +#define SETUP_LISTVIEW_H + +#include "win32.h" +#include <vector> + +// --------------------------------------------------------------------------- +// interface to class ListView +// +// ListView Common Control +// --------------------------------------------------------------------------- + +class ListViewLine +{ + public: + virtual ~ListViewLine() {}; + virtual const std::string get_text(int col) const = 0; + virtual int do_action(int col) = 0; +}; + +typedef std::vector<ListViewLine *> ListViewContents; + +class ListView +{ + public: + class Header + { + public: + const char *text; + int fmt; + int width; + int hdr_width; + }; + typedef Header *HeaderList; + + void init(HWND parent, int id, HeaderList headers); + + void noteColumnWidthStart(); + void noteColumnWidth(int col_num, const std::string& string); + void noteColumnWidthEnd(); + void resizeColumns(void); + + void setContents(ListViewContents *contents); + void setEmptyText(const char *text); + + bool OnNotify (NMHDR *pNmHdr, LRESULT *pResult); + + private: + HWND hWndParent; + HWND hWndListView; + HDC dc; + ListViewContents *contents; + HeaderList headers; + const char *empty_list_text; + + void initColumns(HeaderList hl); + void empty(void); +}; + +#endif /* SETUP_LISTVIEW_H */ diff --git a/Makefile.am b/Makefile.am index 7bd7c57..bce4c8c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -42,17 +42,11 @@ EXTRA_DIST = \ CONTRIBUTORS \ COPYING \ bootstrap.sh \ - check-na.bmp \ - check-no.bmp \ - check-yes.bmp \ - choose-spin.bmp \ cygwin.ico \ cygwin-setup.ico \ cygwin-terminal.ico \ setup.exe.manifest \ - setup64.exe.manifest \ - tree-minus.bmp \ - tree-plus.bmp + setup64.exe.manifest # iniparse.hh is generated from iniparse.yy via bison -d, so it needs to be # included here for proper tracking (but not iniparse.cc, since automake @@ -172,6 +166,8 @@ inilint_SOURCES = \ KeysSetting.h \ libsolv.cc \ libsolv.h \ + ListView.cc \ + ListView.h \ localdir.cc \ localdir.h \ LogFile.cc \ @@ -207,7 +203,6 @@ inilint_SOURCES = \ PackageTrust.h \ PickCategoryLine.cc \ PickCategoryLine.h \ - PickLine.h \ PickPackageLine.cc \ PickPackageLine.h \ PickView.cc \ diff --git a/PickCategoryLine.cc b/PickCategoryLine.cc index e428419..6737454 100644 --- a/PickCategoryLine.cc +++ b/PickCategoryLine.cc @@ -16,134 +16,37 @@ #include "PickCategoryLine.h" #include "package_db.h" #include "PickView.h" +#include "window.h" -void -PickCategoryLine::empty (void) +const std::string +PickCategoryLine::get_text (int col_num) const { - while (bucket.size ()) + if (col_num == pkgname_col) { - PickLine *line = *bucket.begin (); - delete line; - bucket.erase (bucket.begin ()); + std::string s = (cat_tree->collapsed() ? "[+] " : "[-] ") + cat_tree->category().first; + return s; } -} - -void -PickCategoryLine::paint (HDC hdc, HRGN hUpdRgn, int x, int y, int row, int show_cat) -{ - int r = y + row * theView.row_height; - if (show_label) - { - int x2 = x + theView.headers[theView.cat_col].x + HMARGIN / 2 + depth * TREE_INDENT; - int by = r + (theView.tm.tmHeight / 2) - 5; - - // draw the '+' or '-' box - theView.DrawIcon (hdc, x2, by, (collapsed ? theView.bm_treeplus : theView.bm_treeminus)); - - // draw the category name - TextOut (hdc, x2 + 11 + ICON_MARGIN, r, cat.first.c_str(), cat.first.size()); - if (!labellength) - { - SIZE s; - GetTextExtentPoint32 (hdc, cat.first.c_str(), cat.first.size(), &s); - labellength = s.cx; - } - - // draw the 'spin' glyph - spin_x = x2 + 11 + ICON_MARGIN + labellength + ICON_MARGIN; - theView.DrawIcon (hdc, spin_x, by, theView.bm_spin); - - // draw the caption ('Default', 'Install', etc) - TextOut (hdc, spin_x + SPIN_WIDTH + ICON_MARGIN, r, - packagemeta::action_caption (current_default), - strlen (packagemeta::action_caption (current_default))); - row++; - } - if (collapsed) - return; - - // are the siblings containers? - if (bucket.size () && bucket[0]->IsContainer ()) - { - for (size_t n = 0; n < bucket.size (); n++) - { - bucket[n]->paint (hdc, hUpdRgn, x, y, row, show_cat); - row += bucket[n]->itemcount (); - } - } - else + else if (col_num == new_col) { - // calculate the maximum y value we expect for this group of lines - int max_y = y + (row + bucket.size ()) * theView.row_height; - - // paint all contained rows, columnwise - for (int i = 0; theView.headers[i].text; i++) - { - RECT r; - r.left = x + theView.headers[i].x; - r.right = r.left + theView.headers[i].width; - - // set up a clipping mask if necessary - if (theView.headers[i].needs_clip) - IntersectClipRect (hdc, r.left, y, r.right, max_y); - - // draw each row in this column - for (unsigned int n = 0; n < bucket.size (); n++) - { - // test for visibility - r.top = y + ((row + n) * theView.row_height); - r.bottom = r.top + theView.row_height; - if (RectVisible (hdc, &r) != 0) - bucket[n]->paint (hdc, hUpdRgn, (int)r.left, (int)r.top, i, show_cat); - } - - // restore original clipping area - if (theView.headers[i].needs_clip) - SelectClipRgn (hdc, hUpdRgn); - } + return packagemeta::action_caption (cat_tree->action()); } + return ""; } int -PickCategoryLine::click (int const myrow, int const ClickedRow, int const x) +PickCategoryLine::do_action(int col_num) { - if (myrow == ClickedRow && show_label) + if (col_num == pkgname_col) { - if ((size_t) x >= spin_x) - { - current_default = (packagemeta::_actions)((current_default + 1) % 4); - return set_action (current_default); - } - else - { - collapsed = !collapsed; - int accum_row = 0; - for (size_t n = 0; n < bucket.size (); ++n) - accum_row += bucket[n]->itemcount (); - return collapsed ? accum_row : -accum_row; - } + cat_tree->collapsed() = ! cat_tree->collapsed(); + theView.refresh(); } - else + else if (col_num == new_col) { - int accum_row = myrow + (show_label ? 1 : 0); - for (size_t n = 0; n < bucket.size (); ++n) - { - if (accum_row + bucket[n]->itemcount () > ClickedRow) - return bucket[n]->click (accum_row, ClickedRow, x); - accum_row += bucket[n]->itemcount (); - } - return 0; + theView.GetParent ()->SetBusy (); + int u = cat_tree->do_action((packagemeta::_actions)((cat_tree->action() + 1) % 4), theView.deftrust); + theView.GetParent ()->ClearBusy (); + return u; } -} - -int -PickCategoryLine::set_action (packagemeta::_actions action) -{ - theView.GetParent ()->SetBusy (); - current_default = action; - int accum_diff = 0; - for (size_t n = 0; n < bucket.size (); n++) - accum_diff += bucket[n]->set_action (current_default); - theView.GetParent ()->ClearBusy (); - return accum_diff; + return 1; } diff --git a/PickCategoryLine.h b/PickCategoryLine.h index dcffbac..9423eb8 100644 --- a/PickCategoryLine.h +++ b/PickCategoryLine.h @@ -16,75 +16,28 @@ #ifndef SETUP_PICKCATEGORYLINE_H #define SETUP_PICKCATEGORYLINE_H - -class PickView; -#include <vector> -#include "PickLine.h" #include "package_meta.h" +#include "ListView.h" +#include "PickView.h" -class PickCategoryLine:public PickLine +class PickCategoryLine: public ListViewLine { public: - PickCategoryLine (PickView & aView, Category & _cat, size_t thedepth = 0, bool aBool = - true, bool aBool2 = - true):PickLine (_cat.first), - current_default (packagemeta::Default_action), cat (_cat), labellength (0), - depth (thedepth), theView (aView) + PickCategoryLine (PickView & aView, CategoryTree * _tree) : + cat_tree (_tree), + theView (aView) { - if (aBool) - { - collapsed = true; - show_label = true; - } - else - { - collapsed = false; - show_label = aBool2; - } }; ~PickCategoryLine () { - empty (); - } - void ShowLabel (bool aBool = true) - { - show_label = aBool; - if (!show_label) - collapsed = false; - } - virtual void paint (HDC hdc, HRGN hUpdRgn, int x, int y, int row, int show_cat); - virtual int click (int const myrow, int const ClickedRow, int const x); - virtual int itemcount () const - { - if (collapsed) - return 1; - int t = show_label ? 1 : 0; - for (size_t n = 0; n < bucket.size (); ++n) - t += bucket[n]->itemcount (); - return t; - }; - virtual bool IsContainer (void) const - { - return true; - } - virtual void insert (PickLine & aLine) - { - bucket.push_back (&aLine); } - void empty (); - virtual int set_action (packagemeta::_actions); + + const std::string get_text(int col) const; + int do_action(int col); + private: - packagemeta::_actions - current_default; - Category & cat; - bool collapsed; - bool show_label; - size_t labellength; - size_t spin_x; // x-coord where the spin button starts - size_t depth; - PickCategoryLine (PickCategoryLine const &); - PickCategoryLine & operator= (PickCategoryLine const &); - std::vector < PickLine * > bucket; - PickView& theView; + CategoryTree * cat_tree; + PickView & theView; }; + #endif /* SETUP_PICKCATEGORYLINE_H */ diff --git a/PickLine.h b/PickLine.h deleted file mode 100644 index 7cf84cb..0000000 --- a/PickLine.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2002 Robert Collins. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * A copy of the GNU General Public License can be found at - * http://www.gnu.org/ - * - * Written by Robert Collins <robe...@hotmail.com> - * - */ - -#ifndef SETUP_PICKLINE_H -#define SETUP_PICKLINE_H - -#include "win32.h" -#include "package_meta.h" - -class PickLine -{ -public: - virtual void paint (HDC hdc, HRGN hUpdRgn, int x, int y, int col_num, int show_cat) = 0; - virtual int click (int const myrow, int const ClickedRow, int const x) = 0; - virtual int set_action (packagemeta::_actions) = 0; - virtual int itemcount () const = 0; - // this may indicate bad inheritance model. - virtual bool IsContainer (void) const = 0; - virtual void insert (PickLine &) = 0; - const std::string key; - virtual ~ PickLine () - { - }; -protected: - PickLine () - { - }; - PickLine (const std::string& aKey) : key (aKey) - { - }; - PickLine (PickLine const &); - PickLine & operator= (PickLine const &); -}; - -#endif /* SETUP_PICKLINE_H */ diff --git a/PickPackageLine.cc b/PickPackageLine.cc index 2caeafe..b348ab0 100644 --- a/PickPackageLine.cc +++ b/PickPackageLine.cc @@ -17,70 +17,54 @@ #include "PickView.h" #include "package_db.h" -void -PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int show_cat) +const std::string +PickPackageLine::get_text(int col_num) const { - int rb = y + theView.tm.tmHeight; - int by = rb - 11; // top of box images - std::string s; - - if (col_num == theView.current_col && pkg.installed) + if (col_num == pkgname_col) + { + return pkg.name; + } + else if (col_num == current_col) { - TextOut (hdc, x + HMARGIN/2, y, pkg.installed.Canonical_version ().c_str(), - pkg.installed.Canonical_version ().size()); + return pkg.installed.Canonical_version (); } - else if (col_num == theView.new_col) + else if (col_num == new_col) { - // TextOut (hdc, x + HMARGIN/2 + NEW_COL_SIZE_SLOP, y, s.c_str(), s.size()); - // theView.DrawIcon (hdc, x + HMARGIN/2 + ICON_MARGIN/2 + RTARROW_WIDTH, by, theView.bm_spin); - TextOut (hdc, x + HMARGIN/2 + ICON_MARGIN/2 + SPIN_WIDTH , y, - pkg.action_caption ().c_str(), pkg.action_caption ().size()); - theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_spin); + return pkg.action_caption (); } - else if (col_num == theView.bintick_col) + else if (col_num == bintick_col) { + const char *bintick = "?"; if (/* uninstall or skip */ !pkg.desired || /* current version */ pkg.desired == pkg.installed || /* no source */ !pkg.desired.accessible()) - theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkna); + bintick = "n/a"; else if (pkg.picked()) - theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkyes); + bintick = "yes"; else - theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkno); + bintick = "no"; + + return bintick; } - else if (col_num == theView.srctick_col) + else if (col_num == srctick_col) { + const char *srctick = "?"; if ( /* uninstall */ !pkg.desired || - -#if 0 - /* note: I'm not sure what the logic here is. With this following - check enabled, clicking on the "source" box for a package that - is already installed results it in showing "n/a", instead of a - cross-box. That seems very unintuitive, it should show a cross- - box to indicate that the source is going to be downloaded and - unpacked. Disabling this, but leaving the code as reference - in case there is some reason I'm missing for having it. --b.d. */ - /* source only */ (!pkg.desired.picked() - && pkg.desired.sourcePackage().picked() && pkg.desired == pkg.installed) || -#endif - /* when no source mirror available */ - !pkg.desired.sourcePackage().accessible()) - theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkna); + /* when no source mirror available */ + !pkg.desired.sourcePackage().accessible()) + srctick = "n/a"; else if (pkg.srcpicked()) - theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkyes); + srctick = "yes"; else - theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkno); + srctick = "no"; + + return srctick; } - else if (col_num == theView.cat_col) + else if (col_num == cat_col) { - /* shows "first" category - do we want to show any? */ - if (pkg.categories.size () && show_cat) - { - s = pkg.getReadableCategoryList(); - TextOut (hdc, x + HMARGIN / 2, y, s.c_str(), s.size()); - } + return pkg.getReadableCategoryList(); } - else if (col_num == theView.size_col) + else if (col_num == size_col) { int sz = 0; packageversion picked; @@ -103,62 +87,49 @@ PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int sho sz += picked.sourcePackage().source()->size; /* If size still 0, size must be unknown. */ - s = (sz == 0) ? "?" : format_1000s((sz+1023)/1024) + "k"; - SIZE tw; - GetTextExtentPoint32 (hdc, s.c_str(), s.size(), &tw); - int cw = theView.headers[col_num].width - HMARGIN - tw.cx; - TextOut (hdc, x + cw + HMARGIN / 2, y, s.c_str(), s.size()); + std::string size = (sz == 0) ? "?" : format_1000s((sz+1023)/1024) + "k"; + + return size; } - else if (col_num == theView.pkg_col) + else if (col_num == pkg_col) { - s = pkg.name; - if (pkg.SDesc ().size()) - s += std::string(": ") + std::string(pkg.SDesc()); - TextOut (hdc, x + HMARGIN / 2, y, s.c_str(), s.size()); + return pkg.SDesc(); } + + return "unknown"; } int -PickPackageLine::click (int const myrow, int const ClickedRow, int const x) +PickPackageLine::do_action(int col_num) { - // assert (myrow == ClickedRow); - if (x >= theView.headers[theView.new_col].x - HMARGIN / 2 - && x <= theView.headers[theView.new_col + 1].x - HMARGIN / 2) + if (col_num == new_col) { pkg.set_action (theView.deftrust); - return 0; + return 1; } - if (x >= theView.headers[theView.bintick_col].x - HMARGIN / 2 - && x <= theView.headers[theView.bintick_col + 1].x - HMARGIN / 2) + + if (col_num == bintick_col) { if (pkg.desired.accessible ()) - pkg.pick (!pkg.picked ()); + pkg.pick (!pkg.picked ()); } - else if (x >= theView.headers[theView.srctick_col].x - HMARGIN / 2 - && x <= theView.headers[theView.srctick_col + 1].x - HMARGIN / 2) + else if (col_num == srctick_col) { if (pkg.desired.sourcePackage ().accessible ()) - pkg.srcpick (!pkg.srcpicked ()); + pkg.srcpick (!pkg.srcpicked ()); } - /* Unchecking binary while source is unchecked or vice versa is equivalent - to uninstalling. It's essential to set desired correctly, otherwise the + /* Unchecking binary while source is unchecked or vice versa is equivalent to + uninstalling. It's essential to set desired correctly, otherwise the package gets uninstalled without visual feedback to the user. The package will not even show up in the "Pending" view! */ - if ((x >= theView.headers[theView.bintick_col].x - HMARGIN / 2 - && x <= theView.headers[theView.bintick_col + 1].x - HMARGIN / 2) || - (x >= theView.headers[theView.srctick_col].x - HMARGIN / 2 - && x <= theView.headers[theView.srctick_col + 1].x - HMARGIN / 2)) + if ((col_num == bintick_col) || (col_num == srctick_col)) { if (!pkg.picked () && !pkg.srcpicked ()) pkg.desired = packageversion (); + + return 1; } return 0; } - -int PickPackageLine::set_action (packagemeta::_actions action) -{ - pkg.set_action (action, pkg.trustp (true, theView.deftrust)); - return 1; -} diff --git a/PickPackageLine.h b/PickPackageLine.h index 612bf38..a8c3c0d 100644 --- a/PickPackageLine.h +++ b/PickPackageLine.h @@ -17,29 +17,20 @@ #define SETUP_PICKPACKAGELINE_H class PickView; + #include "package_meta.h" -#include "PickLine.h" +#include "ListView.h" -class PickPackageLine:public PickLine +class PickPackageLine: public ListViewLine { public: - PickPackageLine (PickView &aView, packagemeta & apkg):PickLine (apkg.key), pkg (apkg), theView (aView) - { - }; - virtual void paint (HDC hdc, HRGN unused, int x, int y, int col_num, int show_cat); - virtual int click (int const myrow, int const ClickedRow, int const x); - virtual int itemcount () const - { - return 1; - } - virtual bool IsContainer (void) const - { - return false; - } - virtual void insert (PickLine &) + PickPackageLine (PickView &aView, packagemeta & apkg) : + pkg (apkg), + theView (aView) { }; - virtual int set_action (packagemeta::_actions); + const std::string get_text(int col) const; + int do_action(int col); private: packagemeta & pkg; PickView & theView; diff --git a/PickView.cc b/PickView.cc index 57e8f85..967d53b 100644 --- a/PickView.cc +++ b/PickView.cc @@ -14,12 +14,12 @@ */ #include "PickView.h" +#include "PickPackageLine.h" +#include "PickCategoryLine.h" #include <algorithm> #include <limits.h> -#include <commctrl.h> #include <shlwapi.h> -#include "PickPackageLine.h" -#include "PickCategoryLine.h" + #include "package_db.h" #include "dialog.h" #include "resource.h" @@ -28,135 +28,25 @@ #include "LogSingleton.h" #include "Exception.h" -static PickView::Header pkg_headers[] = { - {"Current", 0, 0, true}, - {"New", 0, 0, true}, - {"Bin?", 0, 0, false}, - {"Src?", 0, 0, false}, - {"Categories", 0, 0, true}, - {"Size", 0, 0, true}, - {"Package", 0, 0, true}, - {0, 0, 0, false} -}; - -static PickView::Header cat_headers[] = { - {"Category", 0, 0, true}, - {"Current", 0, 0, true}, - {"New", 0, 0, true}, - {"Bin?", 0, 0, false}, - {"Src?", 0, 0, false}, - {"Size", 0, 0, true}, - {"Package", 0, 0, true}, - {0, 0, 0, false} -}; - -ATOM PickView::WindowClassAtom = 0; - -// DoInsertItem - inserts an item into a header control. -// Returns the index of the new item. -// hwndHeader - handle to the header control. -// iInsertAfter - index of the previous item. -// nWidth - width of the new item. -// lpsz - address of the item string. -static int -DoInsertItem (HWND hwndHeader, int iInsertAfter, int nWidth, LPSTR lpsz) -{ - HDITEM hdi; - int index; - - hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH; - hdi.pszText = lpsz; - hdi.cxy = nWidth; - hdi.cchTextMax = lstrlen (hdi.pszText); - hdi.fmt = HDF_LEFT | HDF_STRING; - - index = SendMessage (hwndHeader, HDM_INSERTITEM, - (WPARAM) iInsertAfter, (LPARAM) & hdi); - - return index; -} - -int -PickView::set_header_column_order (views vm) -{ - if (vm == views::PackageFull || vm == views::PackagePending - || vm == views::PackageKeeps || vm == views::PackageSkips - || vm == views::PackageUserPicked) - { - headers = pkg_headers; - current_col = 0; - new_col = 1; - bintick_col = new_col + 1; - srctick_col = bintick_col + 1; - cat_col = srctick_col + 1; - size_col = cat_col + 1; - pkg_col = size_col + 1; - last_col = pkg_col; - } - else if (vm == views::Category) - { - headers = cat_headers; - cat_col = 0; - current_col = 1; - new_col = current_col + 1; - bintick_col = new_col + 1; - srctick_col = bintick_col + 1; - size_col = srctick_col + 1; - pkg_col = size_col + 1; - last_col = pkg_col; - } - else - return -1; - return last_col; -} - -void -PickView::set_headers () -{ - if (set_header_column_order (view_mode) == -1) - return; - while (int n = SendMessage (listheader, HDM_GETITEMCOUNT, 0, 0)) - { - SendMessage (listheader, HDM_DELETEITEM, n - 1, 0); - } - int i; - for (i = 0; i <= last_col; i++) - DoInsertItem (listheader, i, headers[i].width, (char *) headers[i].text); -} - -void -PickView::note_width (PickView::Header *hdrs, HDC dc, - const std::string& string, int addend, int column) -{ - SIZE s = { 0, 0 }; - - if (string.size()) - GetTextExtentPoint32 (dc, string.c_str(), string.size(), &s); - if (hdrs[column].width < s.cx + addend) - hdrs[column].width = s.cx + addend; -} - void PickView::setViewMode (views mode) { view_mode = mode; - set_headers (); packagedb db; - contents.empty (); + size_t i; + for (i = 0; i < contents.size(); i++) + { + delete contents[i]; + } + contents.clear(); + if (view_mode == PickView::views::Category) { - contents.ShowLabel (true); - /* start collapsed. TODO: make this a chooser flag */ - for (packagedb::categoriesType::iterator n = - packagedb::categories.begin(); n != packagedb::categories.end(); - ++n) - insert_category (&*n, (*n).first.c_str()[0] == '.' - ? CATEGORY_EXPANDED : CATEGORY_COLLAPSED); + insert_category (cat_tree_root); } else { - contents.ShowLabel (false); // iterate through every package for (packagedb::packagecollection::iterator i = db.packages.begin (); i != db.packages.end (); ++i) @@ -175,7 +65,7 @@ PickView::setViewMode (views mode) (pkg.desired && (pkg.picked () || // install bin pkg.srcpicked ())))) // src - + // "Up to date" : installed packages that will not be changed || (view_mode == PickView::views::PackageKeeps && (pkg.installed && pkg.desired && !pkg.picked () @@ -191,29 +81,13 @@ PickView::setViewMode (views mode) { // Filter by package name if (packageFilterString.empty () - || StrStrI (pkg.name.c_str (), packageFilterString.c_str ())) + || StrStrI (pkg.name.c_str (), packageFilterString.c_str ())) insert_pkg (pkg); } } } - RECT r = GetClientRect (); - SCROLLINFO si; - memset (&si, 0, sizeof (si)); - si.cbSize = sizeof (si); - si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; - si.nMin = 0; - si.nMax = headers[last_col].x + headers[last_col].width; // + HMARGIN; - si.nPage = r.right; - SetScrollInfo (GetHWND(), SB_HORZ, &si, TRUE); - - si.nMax = contents.itemcount () * row_height; - si.nPage = r.bottom - header_height; - SetScrollInfo (GetHWND(), SB_VERT, &si, TRUE); - - scroll_ulc_x = scroll_ulc_y = 0; - - InvalidateRect (GetHWND(), &r, TRUE); + listview->setContents(&contents); } PickView::views @@ -259,7 +133,7 @@ isObsolete (std::set <std::string, casecompare_lt_op> &categories) bool isObsolete (const std::string& catname) { - if (casecompare(catname, "ZZZRemovedPackages") == 0 + if (casecompare(catname, "ZZZRemovedPackages") == 0 || casecompare(catname, "_", 1) == 0) return true; return false; @@ -273,120 +147,84 @@ PickView::setObsolete (bool doit) refresh (); } - void PickView::insert_pkg (packagemeta & pkg) { if (!showObsolete && isObsolete (pkg.categories)) return; - PickLine & line = *new PickPackageLine (*this, pkg); - contents.insert (line); + contents.push_back(new PickPackageLine(*this, pkg)); } void -PickView::insert_category (Category *cat, bool collapsed) +PickView::insert_category (CategoryTree *cat_tree) { - // Urk, special case - if (casecompare(cat->first, "All") == 0 || - (!showObsolete && isObsolete (cat->first))) + if (!cat_tree) return; - PickCategoryLine & catline = *new PickCategoryLine (*this, *cat, 1, collapsed); - int packageCount = 0; - for (std::vector <packagemeta *>::iterator i = cat->second.begin (); - i != cat->second.end () ; ++i) - { - if (packageFilterString.empty () \ - || (*i - && StrStrI ((*i)->name.c_str (), packageFilterString.c_str ()))) - { - PickLine & line = *new PickPackageLine (*this, **i); - catline.insert (line); - packageCount++; - } - } - - if (packageFilterString.empty () || packageCount) - contents.insert (catline); - else - delete &catline; -} - -int -PickView::click (int row, int x) -{ - return contents.click (0, row, x); -} + const Category *cat = &(cat_tree->category()); -void -PickView::scroll (HWND hwnd, int which, int *var, int code, int howmany = 1) -{ - SCROLLINFO si; - si.cbSize = sizeof (si); - si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; - GetScrollInfo (hwnd, which, &si); + // Suppress obsolete category when not showing obsolete + if ((!showObsolete && isObsolete (cat->first))) + return; - switch (code) + // if it's not the "All" category + bool hasContents = false; + bool isAll = casecompare(cat->first, "All") == 0; + if (!isAll) { - case SB_THUMBTRACK: - si.nPos = si.nTrackPos; - break; - case SB_THUMBPOSITION: - break; - case SB_BOTTOM: - si.nPos = si.nMax; - break; - case SB_TOP: - si.nPos = 0; - break; - case SB_LINEDOWN: - si.nPos += (row_height * howmany); - break; - case SB_LINEUP: - si.nPos -= (row_height * howmany); - break; - case SB_PAGEDOWN: - si.nPos += si.nPage * 9 / 10; - break; - case SB_PAGEUP: - si.nPos -= si.nPage * 9 / 10; - break; + // count the number of packages in this category + int packageCount = 0; + for (std::vector <packagemeta *>::const_iterator i = cat->second.begin (); + i != cat->second.end () ; ++i) + { + if (packageFilterString.empty () \ + || (*i + && StrStrI ((*i)->name.c_str (), packageFilterString.c_str ()))) + { + packageCount++; + } + } + + // if there are some packages in the category, or we are showing everything, + if (packageFilterString.empty () || packageCount) + { + hasContents = true; + } } - if ((int) si.nPos < 0) - si.nPos = 0; - if (si.nPos + si.nPage > (unsigned int) si.nMax) - si.nPos = si.nMax - si.nPage; - - si.fMask = SIF_POS; - SetScrollInfo (hwnd, which, &si, TRUE); - - int ox = scroll_ulc_x; - int oy = scroll_ulc_y; - *var = si.nPos; - - RECT cr, sr; - ::GetClientRect (hwnd, &cr); - sr = cr; - sr.top += header_height; - UpdateWindow (hwnd); - ScrollWindow (hwnd, ox - scroll_ulc_x, oy - scroll_ulc_y, &sr, &sr); - /* - sr.bottom = sr.top; - sr.top = cr.top; - ScrollWindow (hwnd, ox - scroll_ulc_x, 0, &sr, &sr); - */ - if (ox - scroll_ulc_x) + if (!isAll && !hasContents) + return; + + // insert line for the category + contents.push_back(new PickCategoryLine(*this, cat_tree)); + + // if not collapsed + if (!cat_tree->collapsed()) { - ::GetClientRect (listheader, &cr); - sr = cr; -// UpdateWindow (htmp); - ::MoveWindow (listheader, -scroll_ulc_x, 0, - headers[last_col].x + - headers[last_col].width, header_height, TRUE); + // insert lines for the packages in this category + if (!isAll) + { + for (std::vector <packagemeta *>::const_iterator i = cat->second.begin (); + i != cat->second.end () ; ++i) + { + if (packageFilterString.empty () \ + || (*i + && StrStrI ((*i)->name.c_str (), packageFilterString.c_str ()))) + { + insert_pkg(**i); + } + } + } + + // recurse for contained categories + for (std::vector <CategoryTree *>::iterator i = cat_tree->bucket().begin (); + i != cat_tree->bucket().end(); + i++) + { + insert_category(*i); + } } - UpdateWindow (hwnd); } /* this means to make the 'category' column wide enough to fit the first 'n' @@ -394,72 +232,56 @@ PickView::scroll (HWND hwnd, int which, int *var, int code, int howmany = 1) #define NUM_CATEGORY_COL_WIDTH 2 void -PickView::init_headers (HDC dc) +PickView::init_headers (void) { - int i; - - for (i = 0; headers[i].text; i++) - { - headers[i].width = 0; - headers[i].x = 0; - } - - // A margin of 3*GetSystemMetrics(SM_CXEDGE) is used at each side of the - // header text. (Probably should use that rather than hard-coding HMARGIN - // everywhere) - int addend = 2*3*GetSystemMetrics(SM_CXEDGE); + listview->noteColumnWidthStart(); - // accommodate widths of the 'bin' and 'src' checkbox columns - note_width (headers, dc, headers[bintick_col].text, addend, bintick_col); - note_width (headers, dc, headers[srctick_col].text, addend, srctick_col); + // widths of the 'bin' and 'src' checkbox columns just need to accommodate the + // column name + listview->noteColumnWidth (bintick_col, ""); + listview->noteColumnWidth (srctick_col, ""); - // accomodate the width of each category name + // (In category view) accommodate the width of each category name packagedb db; for (packagedb::categoriesType::iterator n = packagedb::categories.begin(); n != packagedb::categories.end(); ++n) { - if (!showObsolete && isObsolete (n->first)) - continue; - note_width (headers, dc, n->first, HMARGIN, cat_col); + listview->noteColumnWidth (cat_col, n->first); } /* For each package, accomodate the width of the installed version in the - current_col, the widths of all other versions in the new_col, and the - width of the sdesc for the pkg_col. Also, if this is not a Category - view, adjust the 'category' column so that the first NUM_CATEGORY_COL_WIDTH - categories from each package fits. */ + current_col, the widths of all other versions in the new_col, and the width + of the sdesc for the pkg_col and the first NUM_CATEGORY_COL_WIDTH + categories in the category column. */ for (packagedb::packagecollection::iterator n = db.packages.begin (); n != db.packages.end (); ++n) { packagemeta & pkg = *(n->second); - if (!showObsolete && isObsolete (pkg.categories)) - continue; if (pkg.installed) - note_width (headers, dc, pkg.installed.Canonical_version (), - HMARGIN, current_col); + listview->noteColumnWidth (current_col, pkg.installed.Canonical_version ()); for (std::set<packageversion>::iterator i = pkg.versions.begin (); - i != pkg.versions.end (); ++i) - { + i != pkg.versions.end (); ++i) + { if (*i != pkg.installed) - note_width (headers, dc, i->Canonical_version (), - HMARGIN + SPIN_WIDTH, new_col); - std::string z = format_1000s(i->source ()->size); - note_width (headers, dc, z, HMARGIN, size_col); - z = format_1000s(i->sourcePackage ().source ()->size); - note_width (headers, dc, z, HMARGIN, size_col); - } + listview->noteColumnWidth (new_col, i->Canonical_version ()); + std::string z = format_1000s(i->source ()->size); + listview->noteColumnWidth (size_col, z); + z = format_1000s(i->sourcePackage ().source ()->size); + listview->noteColumnWidth (size_col, z); + } std::string s = pkg.name; - if (pkg.SDesc ().size()) - s += std::string (": ") + std::string(pkg.SDesc ()); - note_width (headers, dc, s, HMARGIN, pkg_col); - - if (view_mode != PickView::views::Category && pkg.categories.size () > 2) + listview->noteColumnWidth (pkgname_col, s); + + s = pkg.SDesc (); + listview->noteColumnWidth (pkg_col, s); + + if (pkg.categories.size () > 2) { - std::string compound_cat(""); + std::string compound_cat(""); std::set<std::string, casecompare_lt_op>::const_iterator cat; size_t cnt; - - for (cnt = 0, cat = pkg.categories.begin (); + + for (cnt = 0, cat = pkg.categories.begin (); cnt < NUM_CATEGORY_COL_WIDTH && cat != pkg.categories.end (); ++cat) { @@ -470,507 +292,95 @@ PickView::init_headers (HDC dc) compound_cat += *cat; cnt++; } - note_width (headers, dc, compound_cat, HMARGIN, cat_col); + listview->noteColumnWidth (cat_col, compound_cat); } } - + // ensure that the new_col is wide enough for all the labels - const char *captions[] = { "Uninstall", "Skip", "Reinstall", "Retrieve", + const char *captions[] = { "Uninstall", "Skip", "Reinstall", "Retrieve", "Source", "Keep", NULL }; for (int i = 0; captions[i]; i++) - note_width (headers, dc, captions[i], HMARGIN + SPIN_WIDTH, new_col); - - // finally, compute the actual x values based on widths - headers[0].x = 0; - for (i = 1; i <= last_col; i++) - headers[i].x = headers[i - 1].x + headers[i - 1].width; - // and allow for resizing to ensure the last column reaches - // all the way to the end of the chooser box. - headers[last_col].width += total_delta_x; + listview->noteColumnWidth (cat_col, captions[i]); + + listview->noteColumnWidthEnd(); + listview->resizeColumns(); } -PickView::PickView (Category &cat) : deftrust (TRUST_CURR), -contents (*this, cat, 0, false, true), showObsolete (false), -packageFilterString (), hasWindowRect (false), total_delta_x (0) +PickView::PickView() : + deftrust (TRUST_UNKNOWN), + showObsolete (false), + packageFilterString (), + cat_tree_root (NULL) { } void -PickView::init(views _mode) +PickView::init(views _mode, ListView *_listview, Window *_parent) { - HDC dc = GetDC (GetHWND()); - sysfont = GetStockObject (DEFAULT_GUI_FONT); - SelectObject (dc, sysfont); - GetTextMetrics (dc, &tm); - - bitmap_dc = CreateCompatibleDC (dc); -#define LI(x) LoadImage (hinstance, MAKEINTRESOURCE (x), IMAGE_BITMAP, 0, 0, 0); - bm_spin = LI (IDB_SPIN); - bm_checkyes = LI (IDB_CHECK_YES); - bm_checkno = LI (IDB_CHECK_NO); - bm_checkna = LI (IDB_CHECK_NA); - bm_treeplus = LI (IDB_TREE_PLUS); - bm_treeminus = LI (IDB_TREE_MINUS); -#undef LI - icon_dc = CreateCompatibleDC (dc); - bm_icon = CreateCompatibleBitmap (dc, 11, 11); - SelectObject (icon_dc, bm_icon); - rect_icon = CreateRectRgn (0, 0, 11, 11); - - row_height = (tm.tmHeight + tm.tmExternalLeading + ROW_MARGIN); - int irh = tm.tmExternalLeading + tm.tmDescent + 11 + ROW_MARGIN; - if (row_height < irh) - row_height = irh; - - HDLAYOUT hdl; - WINDOWPOS wp; - - // Ensure that the common control DLL is loaded, and then create - // the header control. - INITCOMMONCONTROLSEX controlinfo = { sizeof (INITCOMMONCONTROLSEX), - ICC_LISTVIEW_CLASSES }; - InitCommonControlsEx (&controlinfo); - - if ((listheader = CreateWindowEx (0, WC_HEADER, (LPCTSTR) NULL, - WS_CHILD | WS_BORDER | CCS_NORESIZE | - // | HDS_BUTTONS - HDS_HORZ, 0, 0, 0, 0, GetHWND(), - (HMENU) IDC_CHOOSE_LISTHEADER, hinstance, - (LPVOID) NULL)) == NULL) - throw new Exception (TOSTRING(__LINE__) " " __FILE__, - "Unable to create list header window", - APPERR_WINDOW_ERROR); - - // Retrieve the bounding rectangle of the parent window's - // client area, and then request size and position values - // from the header control. - RECT rcParent = GetClientRect (); - - hdl.prc = &rcParent; - hdl.pwpos = ℘ - if (!SendMessage (listheader, HDM_LAYOUT, 0, (LPARAM) & hdl)) - throw new Exception (TOSTRING(__LINE__) " " __FILE__, - "Unable to get size and position of rectangle", - APPERR_WINDOW_ERROR); - - // Set the font of the listheader, but don't redraw, because its not shown - // yet.This message does not return a value, so we are not checking it as we - // do above. - SendMessage (listheader, WM_SETFONT, (WPARAM) sysfont, FALSE); - - // Set the size, position, and visibility of the header control. - SetWindowPos (listheader, wp.hwndInsertAfter, wp.x, wp.y, - wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW); - - header_height = wp.cy; - ReleaseDC (GetHWND (), dc); - view_mode = _mode; - refresh (); -} - -PickView::~PickView() -{ - DeleteDC (bitmap_dc); - DeleteObject (bm_spin); - DeleteObject (bm_checkyes); - DeleteObject (bm_checkno); - DeleteObject (bm_checkna); - DeleteObject (bm_treeplus); - DeleteObject (bm_treeminus); - DeleteObject (rect_icon); - DeleteObject (bm_icon); - DeleteDC (icon_dc); -} - -bool PickView::registerWindowClass () -{ - if (WindowClassAtom != 0) - return true; - - // We're not registered yet - WNDCLASSEX wc; - - wc.cbSize = sizeof (wc); - // Some sensible style defaults - wc.style = CS_HREDRAW | CS_VREDRAW; - // Our default window procedure. This replaces itself - // on the first call with the simpler Window::WindowProcReflector(). - wc.lpfnWndProc = Window::FirstWindowProcReflector; - // No class bytes - wc.cbClsExtra = 0; - // One pointer to REFLECTION_INFO in the extra window instance bytes - wc.cbWndExtra = 4; - // The app instance - wc.hInstance = hinstance; //GetInstance (); - // Use a bunch of system defaults for the GUI elements - wc.hIcon = LoadIcon (0, IDI_APPLICATION); - wc.hIconSm = NULL; - wc.hCursor = LoadCursor (0, IDC_ARROW); - wc.hbrBackground = NULL; - // No menu - wc.lpszMenuName = NULL; - // We'll get a little crazy here with the class name - wc.lpszClassName = "listview"; - - // All set, try to register - WindowClassAtom = RegisterClassEx (&wc); - if (WindowClassAtom == 0) - Log (LOG_BABBLE) << "Failed to register listview " << GetLastError () << endLog; - return WindowClassAtom != 0; -} - -LRESULT CALLBACK -PickView::list_vscroll (HWND hwnd, HWND hctl, UINT code, int pos) -{ - scroll (hwnd, SB_VERT, &scroll_ulc_y, code); - return 0; -} - -LRESULT CALLBACK -PickView::list_hscroll (HWND hwnd, HWND hctl, UINT code, int pos) -{ - scroll (hwnd, SB_HORZ, &scroll_ulc_x, code); - return 0; + listview = _listview; + parent = _parent; } void -PickView::set_vscroll_info (const RECT &r) +PickView::build_category_tree() { - SCROLLINFO si; - memset (&si, 0, sizeof (si)); - si.cbSize = sizeof (si); - si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; /* SIF_RANGE was giving strange behaviour */ - si.nMin = 0; - - si.nMax = contents.itemcount () * row_height; - si.nPage = r.bottom - header_height; - - /* if we are under the minimum display count , - * set the offset to 0 - */ - if ((unsigned int) si.nMax <= si.nPage) - scroll_ulc_y = 0; - si.nPos = scroll_ulc_y; - - SetScrollInfo (GetHWND(), SB_VERT, &si, TRUE); -} + /* Build the category tree */ -LRESULT CALLBACK -PickView::list_click (HWND hwnd, BOOL dblclk, int x, int y, UINT hitCode) -{ - int row, refresh __attribute__ ((unused)); - - if (contents.itemcount () == 0) - return 0; - - if (y < header_height) - return 0; - x += scroll_ulc_x; - y += scroll_ulc_y - header_height; - - row = (y + ROW_MARGIN / 2) / row_height; + /* Start collapsed. TODO: make that a flag */ + bool collapsed = true; - if (row < 0 || row >= contents.itemcount ()) - return 0; - - refresh = click (row, x); - - // XXX we need a method to query the database to see if more - // than just one package has changed! Until then... -#if 0 - if (refresh) + /* Dispose of any existing category tree */ + if (cat_tree_root) { -#endif - RECT r = GetClientRect (); - set_vscroll_info (r); - InvalidateRect (GetHWND(), &r, TRUE); -#if 0 - } - else - { - RECT rect; - rect.left = - headers[new_col].x - scroll_ulc_x; - rect.right = - headers[src_col + 1].x - scroll_ulc_x; - rect.top = - header_height + row * row_height - - scroll_ulc_y; - rect.bottom = rect.top + row_height; - InvalidateRect (hwnd, &rect, TRUE); + for (std::vector <CategoryTree *>::const_iterator i = cat_tree_root->bucket().begin(); + i != cat_tree_root->bucket().end(); + i++) + delete *i; + + delete cat_tree_root; + cat_tree_root = NULL; } -#endif - return 0; -} -/* - * LRESULT CALLBACK - * PickView::listview_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) - */ -LRESULT -PickView::WindowProc (UINT message, WPARAM wParam, LPARAM lParam) -{ - int wheel_notches; - UINT wheel_lines; - - switch (message) + /* Find the 'All' category */ + for (packagedb::categoriesType::iterator n = + packagedb::categories.begin(); n != packagedb::categories.end(); + ++n) { - case WM_HSCROLL: - list_hscroll (GetHWND(), (HWND)lParam, LOWORD(wParam), HIWORD(wParam)); - return 0; - case WM_VSCROLL: - list_vscroll (GetHWND(), (HWND)lParam, LOWORD(wParam), HIWORD(wParam)); - return 0; - case WM_MOUSEWHEEL: - // this is how many 'notches' the wheel scrolled, forward/up = positive - wheel_notches = GET_WHEEL_DELTA_WPARAM(wParam) / 120; - - // determine how many lines the user has configred for a mouse scroll - SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_lines, 0); - - if (wheel_lines == 0) // do no scrolling - return 0; - else if (wheel_lines == WHEEL_PAGESCROLL) - scroll (GetHWND (), SB_VERT, &scroll_ulc_y, (wheel_notches > 0) ? - SB_PAGEUP : SB_PAGEDOWN); - else - scroll (GetHWND (), SB_VERT, &scroll_ulc_y, (wheel_notches > 0) ? - SB_LINEUP : SB_LINEDOWN, wheel_lines * abs (wheel_notches)); - return 0; // handled - case WM_LBUTTONDOWN: - list_click (GetHWND(), FALSE, LOWORD(lParam), HIWORD(lParam), wParam); - return 0; - case WM_PAINT: - paint (GetHWND()); - return 0; - case WM_NOTIFY: - { - // pnmh = (LPNMHDR) lParam - LPNMHEADER phdr = (LPNMHEADER) lParam; - switch (phdr->hdr.code) - { - case HDN_ITEMCHANGED: - if (phdr->hdr.hwndFrom == ListHeader ()) - { - if (phdr->pitem && phdr->pitem->mask & HDI_WIDTH) - headers[phdr->iItem].width = phdr->pitem->cxy; - - for (int i = 1; i <= last_col; i++) - headers[i].x = headers[i - 1].x + headers[i - 1].width; - - RECT r = GetClientRect (); - SCROLLINFO si; - si.cbSize = sizeof (si); - si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; - GetScrollInfo (GetHWND(), SB_HORZ, &si); - - int oldMax = si.nMax; - si.nMax = headers[last_col].x + headers[last_col].width; - if (si.nTrackPos && oldMax > si.nMax) - si.nTrackPos += si.nMax - oldMax; - - si.nPage = r.right; - SetScrollInfo (GetHWND(), SB_HORZ, &si, TRUE); - InvalidateRect (GetHWND(), &r, TRUE); - if (si.nTrackPos && oldMax > si.nMax) - scroll (GetHWND(), SB_HORZ, &scroll_ulc_x, SB_THUMBTRACK); - } - break; - } + if (casecompare(n->first, "All") == 0) + { + cat_tree_root = new CategoryTree(*n, collapsed); + break; } - break; - case WM_SIZE: - { - // Note: WM_SIZE msgs only appear when 'just' scrolling the window - RECT windowRect = GetWindowRect (); - if (hasWindowRect) - { - int dx; - if ((dx = windowRect.right - windowRect.left - - lastWindowRect.width ()) != 0) - { - cat_headers[set_header_column_order (views::Category)].width += dx; - pkg_headers[set_header_column_order (views::PackagePending)].width += dx; - set_header_column_order (view_mode); - set_headers (); - ::MoveWindow (listheader, -scroll_ulc_x, 0, - headers[last_col].x + - headers[last_col].width, header_height, TRUE); - total_delta_x += dx; - } - if (windowRect.bottom - windowRect.top - lastWindowRect.height ()) - set_vscroll_info (GetClientRect ()); - } - else - hasWindowRect = true; - - lastWindowRect = windowRect; - return 0; - } - case WM_MOUSEACTIVATE: - SetFocus(GetHWND()); - return MA_ACTIVATE; } - - // default: can't handle this message - return DefWindowProc (GetHWND(), message, wParam, lParam); -} -//// -// Turn black into foreground color and white into background color by -// 1) Filling a square with ~(FG^BG) -// 2) Blitting the bitmap on it with NOTSRCERASE (white->black; black->FG^BG) -// 3) Blitting the result on BG with SRCINVERT (white->BG; black->FG) -void -PickView::DrawIcon (HDC hdc, int x, int y, HANDLE hIcon) -{ - SelectObject (bitmap_dc, hIcon); - FillRgn (icon_dc, rect_icon, bg_fg_brush); - BitBlt (icon_dc, 0, 0, 11, 11, bitmap_dc, 0, 0, NOTSRCERASE); - BitBlt (hdc, x, y, 11, 11, icon_dc, 0, 0, SRCINVERT); -///////////// On WinNT-based systems, we could've done the below instead -///////////// See http://support.microsoft.com/default.aspx?scid=kb;en-us;79212 -// SelectObject (hdc, GetSysColorBrush (COLOR_WINDOWTEXT)); -// HBITMAP bm_icon = CreateBitmap (11, 11, 1, 1, NULL); -// SelectObject (icon_dc, bm_icon); -// BitBlt (icon_dc, 0, 0, 11, 11, bitmap_dc, 0, 0, SRCCOPY); -// MaskBlt (hdc, x2, by, 11, 11, bitmap_dc, 0, 0, bm_icon, 0, 0, MAKEROP4 (SRCAND, PATCOPY)); -// DeleteObject (bm_icon); -} - -void -PickView::paint (HWND hwnd) -{ - // we want to retrieve the update region before calling BeginPaint, - // because after we do that the update region is validated and we can - // no longer retrieve it - HRGN hUpdRgn = CreateRectRgn (0, 0, 0, 0); - - if (GetUpdateRgn (hwnd, hUpdRgn, FALSE) == 0) + /* Add all the other categories as children */ + for (packagedb::categoriesType::iterator n = + packagedb::categories.begin(); n != packagedb::categories.end(); + ++n) { - // error? - return; - } - - // tell the system that we're going to begin painting our window - // it will prevent further WM_PAINT messages from arriving until we're - // done, and if any part of our window was invalidated while we are - // painting, it will retrigger us so that we can fix it - PAINTSTRUCT ps; - HDC hdc = BeginPaint (hwnd, &ps); - - SelectObject (hdc, sysfont); - SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)); - SetBkColor (hdc, GetSysColor (COLOR_WINDOW)); - FillRgn (hdc, hUpdRgn, GetSysColorBrush(COLOR_WINDOW)); - - COLORREF clr = ~GetSysColor (COLOR_WINDOW) ^ GetSysColor (COLOR_WINDOWTEXT); - clr = RGB (GetRValue (clr), GetGValue (clr), GetBValue (clr)); // reconvert - bg_fg_brush = CreateSolidBrush (clr); - - RECT cr; - ::GetClientRect (hwnd, &cr); - - int x = cr.left - scroll_ulc_x; - int y = cr.top - scroll_ulc_y + header_height; - - contents.paint (hdc, hUpdRgn, x, y, 0, (view_mode == - PickView::views::Category) ? 0 : 1); + if (casecompare(n->first, "All") == 0) + continue; - if (contents.itemcount () == 0) - { - static const char *msg = "Nothing to Install/Update"; - if (source == IDC_SOURCE_DOWNLOAD) - msg = "Nothing to Download"; - TextOut (hdc, x + HMARGIN, y, msg, strlen (msg)); + CategoryTree *cat_tree = new CategoryTree(*n, collapsed); + cat_tree_root->bucket().push_back(cat_tree); } - DeleteObject (hUpdRgn); - DeleteObject (bg_fg_brush); - EndPaint (hwnd, &ps); + refresh (); } - -bool -PickView::Create (Window * parent, DWORD Style, RECT *r) +PickView::~PickView() { - - // First register the window class, if we haven't already - if (!registerWindowClass ()) - { - // Registration failed - return false; - } - - // Save our parent, we'll probably need it eventually. - setParent(parent); - - // Create the window instance - CreateWindowEx (// Extended Style - WS_EX_CLIENTEDGE, - // window class atom (name) - "listview", //MAKEINTATOM(WindowClassAtom), - "listviewwindow", // no title-bar string yet - // Style bits - Style, - r ? r->left : CW_USEDEFAULT, - r ? r->top : CW_USEDEFAULT, - r ? r->right - r->left + 1 : CW_USEDEFAULT, - r ? r->bottom - r->top + 1 : CW_USEDEFAULT, - // Parent Window - parent == NULL ? (HWND)NULL : parent->GetHWND (), - // use class menu - (HMENU) MAKEINTRESOURCE (IDC_CHOOSE_LIST), - // The application instance - GetInstance (), - // The this ptr, which we'll use to set up - // the WindowProc reflection. - reinterpret_cast<void *>((Window *)this)); - if (GetHWND() == NULL) - { - Log (LOG_BABBLE) << "Failed to create PickView " << GetLastError () << endLog; - return false; - } - - return true; } void PickView::defaultTrust (trusts trust) { this->deftrust = trust; - - // force the picker to redraw - RECT r = GetClientRect (); - InvalidateRect (this->GetHWND(), &r, TRUE); } -/* This recalculates all column widths and resets the view */ void PickView::refresh() { - HDC dc = GetDC (GetHWND ()); - - // we must set the font of the DC here, otherwise the width calculations - // will be off because the system will use the wrong font metrics - sysfont = GetStockObject (DEFAULT_GUI_FONT); - SelectObject (dc, sysfont); - - // init headers for the current mode - set_headers (); - init_headers (dc); - - // save the current mode - views cur_view_mode = view_mode; - - // switch to the other type and do those headers - view_mode = (view_mode == PickView::views::Category) ? - PickView::views::PackageFull : PickView::views::Category; - set_headers (); - init_headers (dc); - ReleaseDC (GetHWND (), dc); - - view_mode = cur_view_mode; setViewMode (view_mode); } diff --git a/PickView.h b/PickView.h index 298f844..9777d15 100644 --- a/PickView.h +++ b/PickView.h @@ -17,84 +17,17 @@ #define SETUP_PICKVIEW_H #include <string> -#include "win32.h" -#include "window.h" -#include "RECTWrapper.h" - -#define HMARGIN 10 -#define ROW_MARGIN 5 -#define ICON_MARGIN 4 -#define SPIN_WIDTH 11 -#define CHECK_SIZE 11 -#define TREE_INDENT 12 - -#define CATEGORY_EXPANDED 0 -#define CATEGORY_COLLAPSED 1 - -class PickView; -#include "PickCategoryLine.h" + #include "package_meta.h" +#include "ListView.h" + +class Window; +class CategoryTree; -class PickView : public Window +class PickView { public: - virtual bool Create (Window * Parent = NULL, DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN, RECT * r = NULL); - virtual bool registerWindowClass (); - enum class views; - class Header; - int num_columns; - void defaultTrust (trusts trust); - void setViewMode (views mode); - views getViewMode (); - void DrawIcon (HDC hdc, int x, int y, HANDLE hIcon); - void paint (HWND hwnd); - LRESULT CALLBACK list_click (HWND hwnd, BOOL dblclk, int x, int y, UINT hitCode); - LRESULT CALLBACK list_hscroll (HWND hwnd, HWND hctl, UINT code, int pos); - LRESULT CALLBACK list_vscroll (HWND hwnd, HWND hctl, UINT code, int pos); - void set_vscroll_info (const RECT &r); - virtual LRESULT WindowProc (UINT uMsg, WPARAM wParam, LPARAM lParam); - Header *headers; - PickView (Category & cat); - void init(views _mode); - ~PickView(); - static const char *mode_caption (views mode); - void setObsolete (bool doit); - void insert_pkg (packagemeta &); - void insert_category (Category *, bool); - int click (int row, int x); - void refresh(); - int current_col; - int new_col; - int bintick_col; - int srctick_col; - int cat_col; - int size_col; - int pkg_col; - int last_col; - int row_height; - TEXTMETRIC tm; - HDC bitmap_dc, icon_dc; - HBITMAP bm_icon; - HRGN rect_icon; - HBRUSH bg_fg_brush; - HANDLE bm_spin, bm_checkyes, bm_checkno, bm_checkna, bm_treeplus, bm_treeminus; - trusts deftrust; - HANDLE sysfont; - int scroll_ulc_x, scroll_ulc_y; - int header_height; - PickCategoryLine contents; - void scroll (HWND hwnd, int which, int *var, int code, int howmany); - - void SetPackageFilter (const std::string &filterString) - { - packageFilterString = filterString; - } - - - HWND ListHeader (void) const - { - return listheader; - } + trusts deftrust; // XXX: needs accessor enum class views { @@ -106,35 +39,135 @@ public: Category, }; - class Header + PickView (); + ~PickView(); + void defaultTrust (trusts trust); + void setViewMode (views mode); + views getViewMode (); + void init(views _mode, ListView *_listview, Window *parent); + void build_category_tree(); + static const char *mode_caption (views mode); + void setObsolete (bool doit); + void refresh(); + void init_headers (); + + void SetPackageFilter (const std::string &filterString) { - public: - const char *text; - int width; - int x; - bool needs_clip; - }; + packageFilterString = filterString; + } + + Window *GetParent(void) { return parent; } private: - static ATOM WindowClassAtom; - HWND listheader; views view_mode; + ListView *listview; bool showObsolete; std::string packageFilterString; + ListViewContents contents; + CategoryTree *cat_tree_root; + Window *parent; - // Stuff needed to handle resizing - bool hasWindowRect; - RECTWrapper lastWindowRect; - int total_delta_x; + void insert_pkg (packagemeta &); + void insert_category (CategoryTree *); +}; - int set_header_column_order (views vm); - void set_headers (); - void init_headers (HDC dc); - void note_width (Header *hdrs, HDC dc, const std::string& string, - int addend, int column); +enum +{ + pkgname_col = 0, // package/category name + current_col = 1, + new_col = 2, // action + bintick_col = 3, + srctick_col = 4, + cat_col = 5, + size_col = 6, + pkg_col = 7, // desc }; bool isObsolete (std::set <std::string, casecompare_lt_op> &categories); bool isObsolete (const std::string& catname); +// +// Helper class which stores the contents and collapsed/expanded state for each +// category (and the pseudo-category 'All') +// + +class CategoryTree +{ +public: + CategoryTree(Category & cat, bool collapsed) : + _cat (cat), + _collapsed(collapsed), + _action (packagemeta::Default_action) + { + } + + ~CategoryTree() + { + } + + std::vector <CategoryTree *> & bucket() + { + return _bucket; + } + + bool &collapsed() + { + return _collapsed; + } + + const Category &category() + { + return _cat; + } + + packagemeta::_actions & action() + { + return _action; + } + + int do_action(packagemeta::_actions action_id, trusts const deftrust) + { + int u = 1; + _action = action_id; + + if (_bucket.size()) + { + for (std::vector <CategoryTree *>::const_iterator i = _bucket.begin(); + i != _bucket.end(); + i++) + { + // recurse for all contained categories + int l = (*i)->do_action(action_id, deftrust); + + if (!_collapsed) + u += l; + } + } + else + { + // otherwise, this is a leaf category, so apply action to all packages + // in this category + int l = 0; + for (std::vector <packagemeta *>::const_iterator pkg = _cat.second.begin(); + pkg != _cat.second.end(); + ++pkg) + { + (*pkg)->set_action(action_id, (*pkg)->trustp(true, deftrust)); + l++; + } + + // these lines need to be updated, if displayed + if (!_collapsed) + u += l; + } + return u; + } + +private: + Category & _cat; + bool _collapsed; + std::vector <CategoryTree *> _bucket; + packagemeta::_actions _action; +}; + #endif /* SETUP_PICKVIEW_H */ diff --git a/check-na.bmp b/check-na.bmp deleted file mode 100644 index c139e54d514a5b00997d9888d47f9f6d1fdd410b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmZ?r&0>H6J0PV2#N1HK$iN7e&;gT}#Q*>Q8U8<DVE7Kj*$xa0Sq(sZ0D`3z7#RLO IfMAe10G1vT>i_@% diff --git a/check-no.bmp b/check-no.bmp deleted file mode 100644 index 3639605be72d99fb7d2947b24832629609e8007d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 qcmZ?r&0>H6J0PV2#N1HK$iN7e&;gT}#Q*>Q!GiS%7#Ij)kU{`6UJuv+ diff --git a/check-yes.bmp b/check-yes.bmp deleted file mode 100644 index f328dc2fe2899e6a350fb743b3820941ab1c68db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmZ?r&0>H6J0PV2#N1HK$iN7e&;gT}#Q*>Q!GiS%7#QLm7#Q+^bSV&5Lun8nBo2}X F0RRy04qgBN diff --git a/choose-spin.bmp b/choose-spin.bmp deleted file mode 100644 index 8779f6dc20ebfb35c22b7b97afda8fcad8d93664..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmZ?r&0>H6J0PV2#N1HK$iN7e&;gT}#Q*>Q89qK>V0Z___Y)Wx*nyZ$fq|h9h<g|q O814WuNE{>&QU?G_z7YTb diff --git a/choose.cc b/choose.cc index 51d2fb6..c86294a 100644 --- a/choose.cc +++ b/choose.cc @@ -81,7 +81,6 @@ static ControlAdjuster::ControlInfo ChooserControlsInfo[] = { {IDC_CHOOSE_SYNC, CP_RIGHT, CP_TOP}, {IDC_CHOOSE_EXP, CP_RIGHT, CP_TOP}, {IDC_CHOOSE_VIEW, CP_LEFT, CP_TOP}, - {IDC_LISTVIEW_POS, CP_RIGHT, CP_TOP}, {IDC_CHOOSE_VIEWCAPTION, CP_LEFT, CP_TOP}, {IDC_CHOOSE_LIST, CP_STRETCH, CP_STRETCH}, {IDC_CHOOSE_HIDE, CP_LEFT, CP_BOTTOM}, @@ -132,23 +131,33 @@ ChooserPage::~ChooserPage () } } +static ListView::Header pkg_headers[] = { + {"Package", LVCFMT_LEFT}, + {"Current", LVCFMT_LEFT}, + {"New", LVCFMT_LEFT}, + {"Bin?", LVCFMT_LEFT}, + {"Src?", LVCFMT_LEFT}, + {"Categories", LVCFMT_LEFT}, + {"Size", LVCFMT_RIGHT}, + {"Description", LVCFMT_LEFT}, + {0} +}; + void ChooserPage::createListview () { SetBusy (); - static std::vector<packagemeta *> empty_cat; - static Category dummy_cat (std::string ("All"), empty_cat); - chooser = new PickView (dummy_cat); - RECT r = getDefaultListViewSize(); - if (!chooser->Create(this, WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,&r)) - throw new Exception (TOSTRING(__LINE__) " " __FILE__, - "Unable to create chooser list window", - APPERR_WINDOW_ERROR); - chooser->init(PickView::views::Category); - chooser->Show(SW_SHOW); + + listview = new ListView(); + listview->init(GetHWND (), IDC_CHOOSE_LIST, pkg_headers); + + chooser = new PickView(); + chooser->init(PickView::views::Category, listview, this); chooser->setViewMode (!is_new_install || UpgradeAlsoOption || hasManualSelections ? - PickView::views::PackagePending : PickView::views::Category); + PickView::views::PackagePending : PickView::views::Category); + SendMessage (GetDlgItem (IDC_CHOOSE_VIEW), CB_SETCURSEL, (WPARAM)chooser->getViewMode(), 0); + ClearBusy (); } @@ -240,16 +249,6 @@ ChooserPage::setPrompt(char const *aString) ::SetWindowText (GetDlgItem (IDC_CHOOSE_INST_TEXT), aString); } -RECT -ChooserPage::getDefaultListViewSize() -{ - RECT result; - getParentRect (GetHWND (), GetDlgItem (IDC_LISTVIEW_POS), &result); - result.top += 2; - result.bottom -= 2; - return result; -} - void ChooserPage::OnInit () { @@ -336,6 +335,17 @@ ChooserPage::OnActivate() activated = true; } + packagedb::categoriesType::iterator it = db.categories.find("All"); + if (it == db.categories.end ()) + listview->setEmptyText("No packages found."); + if (source == IDC_SOURCE_DOWNLOAD) + listview->setEmptyText("Nothing to download."); + else + listview->setEmptyText("Nothing to install or update."); + + chooser->build_category_tree(); + chooser->init_headers(); + ClearBusy(); chooser->refresh(); @@ -447,7 +457,7 @@ bool ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code) { #if DEBUG - Log (LOG_BABBLE) << "OnMessageCmd " << id << " " << hwndctl << " " << std::hex << code << endLog; + Log (LOG_BABBLE) << "ChooserPage::OnMessageCmd " << id << " " << hwndctl << " " << std::hex << code << endLog; #endif if (id == IDC_CHOOSE_SEARCH_EDIT) @@ -549,10 +559,19 @@ ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code) return false; } -INT_PTR CALLBACK -ChooserPage::OnMouseWheel (UINT message, WPARAM wParam, LPARAM lParam) +bool +ChooserPage::OnNotify (NMHDR *pNmHdr, LRESULT *pResult) { - return chooser->WindowProc (message, wParam, lParam); +#if DEBUG + Log (LOG_BABBLE) << "ChooserPage::OnNotify id:" << pNmHdr->idFrom << " hwnd:" << pNmHdr->hwndFrom << " code:" << (int)pNmHdr->code << endLog; +#endif + + // offer messages to the listview + if (listview->OnNotify(pNmHdr, pResult)) + return true; + + // we don't care + return false; } INT_PTR CALLBACK diff --git a/choose.h b/choose.h index 32a1650..e4953b7 100644 --- a/choose.h +++ b/choose.h @@ -20,6 +20,7 @@ #include "proppage.h" #include "package_meta.h" #include "PickView.h" +#include "ListView.h" #define DEFAULT_TIMER_ID 5 //value doesn't matter, as long as it's unique #define SEARCH_TIMER_DELAY 500 //in milliseconds @@ -33,8 +34,7 @@ public: ~ChooserPage (); virtual bool OnMessageCmd (int id, HWND hwndctl, UINT code); - virtual INT_PTR CALLBACK OnMouseWheel (UINT message, WPARAM wParam, - LPARAM lParam); + virtual bool OnNotify (NMHDR *pNmHdr, LRESULT *pResult); virtual INT_PTR CALLBACK OnTimerMessage (UINT message, WPARAM wParam, LPARAM lparam); @@ -51,7 +51,6 @@ public: } private: void createListview (); - RECT getDefaultListViewSize(); void getParentRect (HWND parent, HWND child, RECT * r); void keepClicked(); void changeTrust(int button, bool test, bool initial); @@ -63,6 +62,7 @@ private: void initialUpdateState(); PickView *chooser; + ListView *listview; static HWND ins_dialog; bool cmd_show_set; bool saved_geom; diff --git a/main.cc b/main.cc index 1374fb6..8ceaa4d 100644 --- a/main.cc +++ b/main.cc @@ -145,7 +145,7 @@ main_display () // Initialize common controls INITCOMMONCONTROLSEX icce = { sizeof (INITCOMMONCONTROLSEX), - ICC_WIN95_CLASSES }; + ICC_WIN95_CLASSES | ICC_LISTVIEW_CLASSES }; InitCommonControlsEx (&icce); // Initialize COM and ShellLink instance here. For some reason diff --git a/res.rc b/res.rc index 02b60cf..27f0378 100644 --- a/res.rc +++ b/res.rc @@ -1,5 +1,6 @@ #include "resource.h" #include "windows.h" +#include "commctrl.h" #define SETUP_STANDARD_DIALOG_W 339 #define SETUP_STANDARD_DIALOG_H 179 @@ -357,8 +358,8 @@ BEGIN SETUP_EXP_X, 30, SETUP_KPCE_W, 14 CONTROL "", IDC_HEADSEPARATOR, "Static", SS_BLACKFRAME | SS_SUNKEN, 0, 28, SETUP_STANDARD_DIALOG_W, 1 - CONTROL "", IDC_LISTVIEW_POS, "Static", SS_BLACKFRAME | NOT - WS_VISIBLE, 7, 45, SETUP_STANDARD_DIALOG_W - 14, 122 + CONTROL "", IDC_CHOOSE_LIST, WC_LISTVIEW, LVS_NOSORTHEADER | LVS_REPORT | LVS_SINGLESEL, + 7, 47, SETUP_STANDARD_DIALOG_W - 14, 120, WS_EX_CLIENTEDGE CONTROL "&Hide obsolete packages", IDC_CHOOSE_HIDE, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 7, 167, 160, 14 ICON IDI_CYGWIN, IDC_HEADICON, SETUP_HEADICON_X, 0, 21, 20 @@ -528,18 +529,6 @@ CYGWIN-SETUP.ICON FILE DISCARDABLE "cygwin-setup.ico" CYGWIN.ICON FILE DISCARDABLE "cygwin.ico" CYGWIN-TERMINAL.ICON FILE DISCARDABLE "cygwin-terminal.ico" -///////////////////////////////////////////////////////////////////////////// -// -// Bitmap -// - -IDB_SPIN BITMAP DISCARDABLE "choose-spin.bmp" -IDB_CHECK_YES BITMAP DISCARDABLE "check-yes.bmp" -IDB_CHECK_NO BITMAP DISCARDABLE "check-no.bmp" -IDB_CHECK_NA BITMAP DISCARDABLE "check-na.bmp" -IDB_TREE_PLUS BITMAP DISCARDABLE "tree-plus.bmp" -IDB_TREE_MINUS BITMAP DISCARDABLE "tree-minus.bmp" - ///////////////////////////////////////////////////////////////////////////// // // String Table diff --git a/resource.h b/resource.h index 421a24c..2f1036b 100644 --- a/resource.h +++ b/resource.h @@ -71,15 +71,6 @@ #define IDD_DOWNLOAD_ERROR 224 #define IDD_CONFIRM 225 -// Bitmaps - -#define IDB_SPIN 300 -#define IDB_CHECK_YES 301 -#define IDB_CHECK_NO 302 -#define IDB_CHECK_NA 303 -#define IDB_TREE_PLUS 304 -#define IDB_TREE_MINUS 305 - // icons #define IDI_CYGWIN_SETUP 401 @@ -118,7 +109,6 @@ #define IDC_NET_USER 527 #define IDC_NET_PASSWD 528 #define IDC_VERSION 529 -#define IDC_LISTVIEW_POS 530 #define IDC_CHOOSE_VIEW 531 #define IDC_CHOOSE_EXP 532 #define IDC_CHOOSE_BEST 533 @@ -135,7 +125,6 @@ #define IDC_DLS_IPROGRESS_TEXT 545 #define IDC_CHOOSE_INST_TEXT 546 #define IDC_CHOOSE_VIEWCAPTION 547 -#define IDC_CHOOSE_LISTHEADER 548 #define IDC_INS_BL_PACKAGE 549 #define IDC_INS_BL_TOTAL 550 #define IDC_INS_BL_DISK 551 diff --git a/tree-minus.bmp b/tree-minus.bmp deleted file mode 100644 index 35b2221b89542e9c1b37b10969107f3fc0f72362..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmZ?r&0>H6J0PV2#N1HK$iN7e&;gT}#Q*>Q8U8<DU}#WaVA#I^ffpdLLE<2JkU9W? C))N^3 diff --git a/tree-plus.bmp b/tree-plus.bmp deleted file mode 100644 index e0335d970ffda606e31f76a84d1c56298106e4bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmZ?r&0>H6J0PV2#N1HK$iN7e&;gT}#Q*>Q8U8<DU}#WaVA#KafnhfkFF<00#6j{P FbpU`?6A1tS -- 2.17.0