From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13833 invoked by alias); 5 Aug 2018 22:10:30 -0000 Mailing-List: contact cygwin-apps-help@cygwin.com; run by ezmlm Precedence: bulk Sender: cygwin-apps-owner@cygwin.com List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Mail-Followup-To: cygwin-apps@cygwin.com Received: (qmail 13740 invoked by uid 89); 5 Aug 2018 22:10:29 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-24.7 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_LAZY_DOMAIN_SECURITY,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 spammy=14,6 X-HELO: rgout03.bt.lon5.cpcloud.co.uk Received: from rgout0301.bt.lon5.cpcloud.co.uk (HELO rgout03.bt.lon5.cpcloud.co.uk) (65.20.0.207) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 05 Aug 2018 22:10:26 +0000 X-OWM-Source-IP: 86.151.121.200 (GB) X-OWM-Env-Sender: jonturney@btinternet.com X-VadeSecure-score: verdict=clean score=0/300, class=clean X-SNCR-VADESECURE: CLEAN Received: from localhost.localdomain (86.151.121.200) by rgout03.bt.lon5.cpcloud.co.uk (9.0.019.26-1) (authenticated as jonturney@btinternet.com) id 5B4CE16C01FB5C53; Sun, 5 Aug 2018 23:10:25 +0100 From: Jon Turney To: cygwin-apps@cygwin.com Cc: Jon Turney Subject: [PATCH setup 07/13] Custom draw popup menus in ListView control Date: Sun, 05 Aug 2018 22:10:00 -0000 Message-Id: <20180805220851.270212-8-jon.turney@dronecode.org.uk> In-Reply-To: <20180805220851.270212-1-jon.turney@dronecode.org.uk> References: <20180805220851.270212-1-jon.turney@dronecode.org.uk> X-SW-Source: 2018-08/txt/msg00015.txt.bz2 Construct a menu containing the actions from the action list for the package or category, and if one is selected, apply it. This lets us remove packagemeta::set_action() which implements the strange UX of cycling around actions without showing what the possibilities are. This somewhat emulates a BS_SPLITBUTTON style control. The 'popup' cell has a visual hint that clicking on it opens a menu (a 'combox scrollbar'), and the popup menu is located at the position of the click. v2: Factor out popup_menu, for future use by keyboard accelerators --- ListView.cc | 105 +++++++++++++++++++++++++++++++++++++++++--- ListView.h | 6 ++- PickCategoryLine.cc | 20 ++++++++- PickCategoryLine.h | 3 +- PickPackageLine.cc | 16 +++++-- PickPackageLine.h | 3 +- choose.cc | 2 +- package_meta.cc | 64 --------------------------- package_meta.h | 1 - 9 files changed, 139 insertions(+), 81 deletions(-) diff --git a/ListView.cc b/ListView.cc index e3f1e44..a555caa 100644 --- a/ListView.cc +++ b/ListView.cc @@ -274,18 +274,31 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult) #endif int iRow = pNmItemAct->iItem; int iCol = pNmItemAct->iSubItem; + if (iRow < 0) + return false; - if (iRow >= 0) + int update = 0; + + if (headers[iCol].type == ListView::ControlType::popup) + { + POINT p; + // position pop-up menu at the location of the click + GetCursorPos(&p); + + update = popup_menu(iRow, iCol, p); + } + else { // Inform the item of the click - int update = (*contents)[iRow]->do_action(iCol); + update = (*contents)[iRow]->do_action(iCol, 0); + } - // Update items, if needed - if (update > 0) - { - ListView_RedrawItems(hWndListView, iRow, iRow + update -1); - } + // Update items, if needed + if (update > 0) + { + ListView_RedrawItems(hWndListView, iRow, iRow + update -1); } + return true; } break; @@ -346,6 +359,41 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult) result = CDRF_SKIPDEFAULT; } break; + + case ListView::ControlType::popup: + { + // let the control draw the text, but notify us afterwards + result = CDRF_NOTIFYPOSTPAINT; + } + break; + } + + *pResult = result; + return true; + } + case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT: + { + LRESULT result = CDRF_DODEFAULT; + int iCol = pNmLvCustomDraw->iSubItem; + int iRow = pNmLvCustomDraw->nmcd.dwItemSpec; + + switch (headers[iCol].type) + { + default: + result = CDRF_DODEFAULT; + break; + + case ListView::ControlType::popup: + { + // draw the control at the RHS of the cell + RECT r; + ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r); + r.left = r.right - GetSystemMetrics(SM_CXVSCROLL); + DrawFrameControl(pNmLvCustomDraw->nmcd.hdc, &r, DFC_SCROLL,DFCS_SCROLLCOMBOBOX); + + result = CDRF_DODEFAULT; + } + break; } *pResult = result; return true; @@ -369,3 +417,46 @@ ListView::setEmptyText(const char *text) { empty_list_text = text; } + +int +ListView::popup_menu(int iRow, int iCol, POINT p) +{ + int update = 0; + // construct menu + HMENU hMenu = CreatePopupMenu(); + + MENUITEMINFO mii; + memset(&mii, 0, sizeof(mii)); + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_STRING | MIIM_ID; + mii.fType = MFT_STRING; + + ActionList *al = (*contents)[iRow]->get_actions(iCol); + + Actions::iterator i; + int j = 1; + for (i = al->list.begin (); i != al->list.end (); ++i, ++j) + { + BOOL res; + mii.dwTypeData = (char *)i->name.c_str(); + mii.fState = (i->selected ? MFS_CHECKED : MFS_UNCHECKED | + i->enabled ? MFS_ENABLED : MFS_DISABLED); + mii.wID = j; + + res = InsertMenuItem(hMenu, -1, TRUE, &mii); + if (!res) Log (LOG_BABBLE) << "InsertMenuItem failed " << endLog; + } + + int id = TrackPopupMenu(hMenu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NOANIMATION, + p.x, p.y, 0, hWndListView, NULL); + + // Inform the item of the menu choice + if (id) + update = (*contents)[iRow]->do_action(iCol, al->list[id-1].id); + + DestroyMenu(hMenu); + delete al; + + return update; +} diff --git a/ListView.h b/ListView.h index 3aabc3f..b14777c 100644 --- a/ListView.h +++ b/ListView.h @@ -14,6 +14,7 @@ #ifndef SETUP_LISTVIEW_H #define SETUP_LISTVIEW_H +#include "ActionList.h" #include "win32.h" #include @@ -28,7 +29,8 @@ class ListViewLine public: virtual ~ListViewLine() {}; virtual const std::string get_text(int col) const = 0; - virtual int do_action(int col) = 0; + virtual ActionList *get_actions(int col) const = 0; + virtual int do_action(int col, int id) = 0; }; typedef std::vector ListViewContents; @@ -40,6 +42,7 @@ class ListView { text, checkbox, + popup, }; class Header @@ -75,6 +78,7 @@ class ListView void initColumns(HeaderList hl); void empty(void); + int popup_menu(int iRow, int iCol, POINT p); }; #endif /* SETUP_LISTVIEW_H */ diff --git a/PickCategoryLine.cc b/PickCategoryLine.cc index 6737454..ec15b4a 100644 --- a/PickCategoryLine.cc +++ b/PickCategoryLine.cc @@ -17,6 +17,7 @@ #include "package_db.h" #include "PickView.h" #include "window.h" +#include "package_meta.h" const std::string PickCategoryLine::get_text (int col_num) const @@ -34,7 +35,7 @@ PickCategoryLine::get_text (int col_num) const } int -PickCategoryLine::do_action(int col_num) +PickCategoryLine::do_action(int col_num, int action_id) { if (col_num == pkgname_col) { @@ -44,9 +45,24 @@ PickCategoryLine::do_action(int col_num) else if (col_num == new_col) { theView.GetParent ()->SetBusy (); - int u = cat_tree->do_action((packagemeta::_actions)((cat_tree->action() + 1) % 4), theView.deftrust); + int u = cat_tree->do_action((packagemeta::_actions)(action_id), theView.deftrust); theView.GetParent ()->ClearBusy (); return u; } return 1; } + +ActionList * +PickCategoryLine::get_actions(int col) const +{ + ActionList *al = new ActionList(); + packagemeta::_actions current_default = cat_tree->action(); + + al->add("Default", (int)packagemeta::Default_action, (current_default == packagemeta::Default_action), TRUE); + al->add("Install", (int)packagemeta::Install_action, (current_default == packagemeta::Install_action), TRUE); + al->add(packagedb::task == PackageDB_Install ? "Reinstall" : "Retrieve", + (int)packagemeta::Reinstall_action, (current_default == packagemeta::Reinstall_action), TRUE); + al->add("Uninstall", (int)packagemeta::Uninstall_action, (current_default == packagemeta::Uninstall_action), TRUE); + + return al; +} diff --git a/PickCategoryLine.h b/PickCategoryLine.h index 9423eb8..6c18018 100644 --- a/PickCategoryLine.h +++ b/PickCategoryLine.h @@ -33,7 +33,8 @@ public: } const std::string get_text(int col) const; - int do_action(int col); + ActionList *get_actions(int col) const; + int do_action(int col, int action_id); private: CategoryTree * cat_tree; diff --git a/PickPackageLine.cc b/PickPackageLine.cc index b348ab0..67baad2 100644 --- a/PickPackageLine.cc +++ b/PickPackageLine.cc @@ -100,14 +100,13 @@ PickPackageLine::get_text(int col_num) const } int -PickPackageLine::do_action(int col_num) +PickPackageLine::do_action(int col_num, int action_id) { if (col_num == new_col) { - pkg.set_action (theView.deftrust); + pkg.select_action(action_id, theView.deftrust); return 1; } - if (col_num == bintick_col) { if (pkg.desired.accessible ()) @@ -133,3 +132,14 @@ PickPackageLine::do_action(int col_num) return 0; } + +ActionList * +PickPackageLine::get_actions(int col_num) const +{ + if (col_num == new_col) + { + return pkg.list_actions (theView.deftrust); + } + + return NULL; +} diff --git a/PickPackageLine.h b/PickPackageLine.h index a8c3c0d..7d96d44 100644 --- a/PickPackageLine.h +++ b/PickPackageLine.h @@ -30,7 +30,8 @@ public: { }; const std::string get_text(int col) const; - int do_action(int col); + ActionList *get_actions(int col_num) const; + int do_action(int col, int action_id); private: packagemeta & pkg; PickView & theView; diff --git a/choose.cc b/choose.cc index c65a107..d40e824 100644 --- a/choose.cc +++ b/choose.cc @@ -134,7 +134,7 @@ ChooserPage::~ChooserPage () static ListView::Header pkg_headers[] = { {"Package", LVCFMT_LEFT, ListView::ControlType::text}, {"Current", LVCFMT_LEFT, ListView::ControlType::text}, - {"New", LVCFMT_LEFT, ListView::ControlType::text}, + {"New", LVCFMT_LEFT, ListView::ControlType::popup}, {"Bin?", LVCFMT_LEFT, ListView::ControlType::checkbox}, {"Src?", LVCFMT_LEFT, ListView::ControlType::checkbox}, {"Categories", LVCFMT_LEFT, ListView::ControlType::text}, diff --git a/package_meta.cc b/package_meta.cc index f55c3f3..4f7d39a 100644 --- a/package_meta.cc +++ b/package_meta.cc @@ -423,70 +423,6 @@ packagemeta::action_caption () const return desired.Canonical_version (); } -/* Set the next action given a current action. */ -void -packagemeta::set_action (trusts const trust) -{ - set::iterator i; - - /* Keep the picked settings of the former desired version, if any, and make - sure at least one of them is picked. If both are unpicked, pick the - binary version. */ - bool source_picked = desired && srcpicked (); - bool binary_picked = !desired || picked () || !source_picked; - - /* If we're on "Keep" on the installed version, and the version is available, - switch to "Reinstall". */ - if (desired && desired == installed && !picked () - && desired.accessible ()) - { - pick (true); - return; - } - - if (!desired) - { - /* From "Uninstall" switch to the first version. From "Skip" switch to - the first version as well, unless the user picks for the first time. - In that case switch to the trustp version immediately. */ - if (installed || user_picked) - i = versions.begin (); - else - for (i = versions.begin (); - i != versions.end () && *i != trustp (false, trust); - ++i) - ; - } - else - { - /* Otherwise switch to the next version. */ - for (i = versions.begin (); i != versions.end () && *i != desired; ++i) - ; - ++i; - } - /* If there's another version in the list, switch to it, otherwise - switch to "Uninstall". */ - if (i != versions.end ()) - { - desired = *i; - /* If the next version is the installed version, unpick it. This will - have the desired effect to show the package in "Keep" mode. See also - above for the code switching to "Reinstall". */ - pick (desired != installed && binary_picked); - srcpick (desired.sourcePackage().accessible () && source_picked); - } - else - { - desired = packageversion (); - pick(false); - srcpick(false); - } - - /* Memorize the fact that the user picked at least once. */ - if (!installed) - user_picked = true; -} - void packagemeta::select_action (int id, trusts const deftrust) { diff --git a/package_meta.h b/package_meta.h index 0f01837..8a42319 100644 --- a/package_meta.h +++ b/package_meta.h @@ -60,7 +60,6 @@ public: }; static const char *action_caption (_actions value); - void set_action (trusts const t); void set_action (_actions, packageversion const & default_version); ActionList *list_actions(trusts const trust); void select_action (int id, trusts const deftrust); -- 2.17.0