From: Jon Turney <jon.turney@dronecode.org.uk>
To: cygwin-apps@cygwin.com
Cc: Jon Turney <jon.turney@dronecode.org.uk>
Subject: [PATCH setup 04/13] Use a ListView common control rather than a hand-built grid
Date: Sun, 05 Aug 2018 22:10:00 -0000 [thread overview]
Message-ID: <20180805220851.270212-5-jon.turney@dronecode.org.uk> (raw)
In-Reply-To: <20180805220851.270212-1-jon.turney@dronecode.org.uk>
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 <robertc@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
next prev parent reply other threads:[~2018-08-05 22:10 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-08-05 22:09 [PATCH setup 00/13] ListView Package Chooser Jon Turney
2018-08-05 22:09 ` [PATCH setup 03/13] Drop 'using namespace std;' from PickView.cc Jon Turney
2018-08-05 22:09 ` [PATCH setup 01/13] Change packagemeta::_actions to an enum Jon Turney
2018-08-05 22:09 ` [PATCH setup 02/13] Add OnNotify virtual function to class Window for WM_NOTIFY notifications Jon Turney
2018-08-05 22:10 ` [PATCH setup 05/13] Custom draw checkboxes in ListView control Jon Turney
2018-08-05 22:10 ` Jon Turney [this message]
2018-08-05 22:10 ` [PATCH setup 07/13] Custom draw popup menus " Jon Turney
2018-08-05 22:10 ` [PATCH setup 06/13] Add methods for listing possible actions on, and applying one to, a package Jon Turney
2018-08-05 22:10 ` [PATCH setup 08/13] Show the count of packages in a category Jon Turney
2018-08-05 22:10 ` [PATCH setup 09/13] Use an icon to represent expanded/collapsed state Jon Turney
2018-08-05 22:11 ` [PATCH setup 10/13] Use indents in category view Jon Turney
2018-08-05 22:12 ` [PATCH setup 13/13] Add ldesc tooltips to sdesc column of listview Jon Turney
2018-08-05 22:12 ` [PATCH setup 11/13] Add LDesc() accessor method to SolvableVersion Jon Turney
2018-08-05 22:12 ` [PATCH setup 12/13] Restore packagemeta::LDesc() Jon Turney
2018-08-06 14:15 ` [PATCH setup 00/13] ListView Package Chooser Ken Brown
2018-08-06 16:41 ` Achim Gratz
2018-08-06 16:47 ` Achim Gratz
2018-08-06 19:19 ` Ken Brown
2018-10-13 18:46 ` Jon Turney
2018-08-06 16:40 ` Achim Gratz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180805220851.270212-5-jon.turney@dronecode.org.uk \
--to=jon.turney@dronecode.org.uk \
--cc=cygwin-apps@cygwin.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).