diff options
Diffstat (limited to 'src/windows/identity/ui/notifier.c')
-rw-r--r-- | src/windows/identity/ui/notifier.c | 3008 |
1 files changed, 2437 insertions, 571 deletions
diff --git a/src/windows/identity/ui/notifier.c b/src/windows/identity/ui/notifier.c index 9804abf..e0582eb 100644 --- a/src/windows/identity/ui/notifier.c +++ b/src/windows/identity/ui/notifier.c @@ -17,7 +17,7 @@ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * ACTION OF CONTRACT TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
@@ -31,13 +31,70 @@ #define KHUI_NOTIFIER_CLASS L"KhuiNotifierMsgWindowClass"
#define KHUI_ALERTER_CLASS L"KhuiAlerterWindowClass"
+#define KHUI_ALERTBIN_CLASS L"KhuiAlertBinWindowClass"
#define KHUI_NOTIFIER_WINDOW L"KhuiNotifierMsgWindow"
+
+/* The commands that are available as default actions when the user
+ clicks the notification icon. */
+
+khm_int32 khm_notifier_actions[] = {
+ KHUI_ACTION_OPEN_APP,
+ KHUI_ACTION_NEW_CRED
+};
+
+khm_size n_khm_notifier_actions = ARRAYLENGTH(khm_notifier_actions);
+
/* notifier message for notification icon */
#define KHUI_WM_NOTIFIER WM_COMMAND
-#define KHUI_ALERT_QUEUE_MAX 64
+#define DRAWTEXTOPTIONS (DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK)
+
+/* are we showing an alert? */
+#define ALERT_DISPLAYED() (balloon_alert != NULL || khui_alert_windows != NULL)
+
+/* Forward declarations */
+
+struct tag_alerter_wnd_data;
+typedef struct tag_alerter_wnd_data alerter_wnd_data;
+
+struct tag_alert_list;
+typedef struct tag_alert_list alert_list;
+
+static khm_int32
+alert_show(khui_alert * a);
+
+static khm_int32
+alert_show_minimized(khui_alert * a);
+
+static khm_int32
+alert_show_normal(khui_alert * a);
+
+static khm_int32
+alert_show_list(alert_list * alist);
+
+static khm_int32
+alert_enqueue(khui_alert * a);
+
+static khm_boolean
+alert_is_equal(khui_alert * a1, khui_alert * a2);
+
+static void
+check_for_queued_alerts(void);
+
+static void
+show_queued_alerts(void);
+
+static khm_int32
+alert_consolidate(alert_list * alist,
+ khui_alert * alert,
+ khm_boolean add_from_queue);
+
+static khm_int32
+get_default_notifier_action(void);
+
+/* Globals */
/* window class registration atom for message only notifier window
class */
@@ -45,35 +102,57 @@ ATOM atom_notifier = 0; /* window class registration atom for alert windows */
ATOM atom_alerter = 0;
+/* window class registration atom for the alert "bin", which is the
+ window that holds all the alerts. */
+ATOM atom_alert_bin = 0;
/* notifier message window */
HWND hwnd_notifier = NULL;
BOOL notifier_ready = FALSE;
-khm_boolean notifier_modal_loop = FALSE;
+/* The list of alert windows currently active */
+alerter_wnd_data * khui_alert_windows = NULL;
+
+/* Notification icon for when there are no alerts to be displayed */
+int iid_normal = IDI_NOTIFY_NONE;
-khui_alert * current_alert = NULL;
+/* The alert currently being displayed in a balloon */
+khui_alert * balloon_alert = NULL;
+
+/**********************************************************************
+ Alert Queue
+
+ The alert queue is the data structure that keeps track of all the
+ alerts that are waiting to be displayed. Alerts will be placed on
+ the queue if they cannot be immediately displayed for some reason
+ (e.g. another alert is being displayed, or the user is working in
+ another window).
+***********************************************************************/
+
+#define KHUI_ALERT_QUEUE_MAX 64
khui_alert * alert_queue[KHUI_ALERT_QUEUE_MAX];
khm_int32 alert_queue_head = 0;
khm_int32 alert_queue_tail = 0;
-int iid_normal = IDI_NOTIFY_NONE;
-
#define is_alert_queue_empty() (alert_queue_head == alert_queue_tail)
#define is_alert_queue_full() (((alert_queue_tail + 1) % KHUI_ALERT_QUEUE_MAX) == alert_queue_head)
+/* NOTE: the alert queue functions are unsafe to call from any thread
+ other than the UI thread. */
+
static void
-add_to_alert_queue(khui_alert * a) {
+alert_queue_put_alert(khui_alert * a) {
if (is_alert_queue_full()) return;
alert_queue[alert_queue_tail++] = a;
khui_alert_hold(a);
alert_queue_tail %= KHUI_ALERT_QUEUE_MAX;
}
+/* the caller needs to release the alert that's returned */
static khui_alert *
-del_from_alert_queue(void) {
+alert_queue_get_alert(void) {
khui_alert * a;
if (is_alert_queue_empty()) return NULL;
@@ -83,57 +162,140 @@ del_from_alert_queue(void) { return a; /* held */
}
-static khui_alert *
-peek_alert_queue(void) {
- if (is_alert_queue_empty()) return NULL;
- return alert_queue[alert_queue_head];
+static int
+alert_queue_get_size(void) {
+ if (is_alert_queue_empty())
+ return 0;
+
+ if (alert_queue_tail < alert_queue_head) {
+ return (alert_queue_tail + KHUI_ALERT_QUEUE_MAX - alert_queue_head);
+ } else {
+ return alert_queue_tail - alert_queue_head;
+ }
}
-static void
-check_for_queued_alerts(void) {
- if (!is_alert_queue_empty()) {
- khui_alert * a;
+static khui_alert *
+alert_queue_get_alert_by_pos(int pos) {
+ khui_alert * a;
- a = peek_alert_queue();
+ if (is_alert_queue_empty() ||
+ pos >= alert_queue_get_size() ||
+ pos < 0) {
+ return NULL;
+ }
- if (a->title) {
- HICON hi;
- int res;
+ a = alert_queue[(alert_queue_head + pos) % KHUI_ALERT_QUEUE_MAX];
+ if (a) {
+ khui_alert_hold(a);
+ }
+ return a;
+}
- if (a->severity == KHERR_ERROR)
- res = OIC_ERROR;
- else if (a->severity == KHERR_WARNING)
- res = OIC_WARNING;
- else
- res = OIC_INFORMATION;
+static int
+alert_queue_delete_alert(khui_alert * a) {
+ int idx;
+ int succ;
- hi = LoadImage(0, MAKEINTRESOURCE(res),
- IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
- LR_SHARED);
+ idx = alert_queue_head;
+ while(idx != alert_queue_tail) {
+ if (alert_queue[idx] == a)
+ break;
- khm_statusbar_set_part(KHUI_SBPART_NOTICE,
- hi,
- a->title);
- }
- } else {
- khm_statusbar_set_part(KHUI_SBPART_NOTICE,
- NULL, NULL);
+ idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;
}
+
+ if (idx == alert_queue_tail)
+ return 0;
+
+#ifdef DEBUG
+ assert(alert_queue[idx]);
+#endif
+ khui_alert_release(alert_queue[idx]);
+
+ succ = (idx + 1) % KHUI_ALERT_QUEUE_MAX;
+ while(succ != alert_queue_tail) {
+ alert_queue[idx] = alert_queue[succ];
+
+ succ = (succ + 1) % KHUI_ALERT_QUEUE_MAX;
+ idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;
+ }
+
+ alert_queue_tail = idx;
+ return 1;
+}
+
+/* the caller needs to release the alert that's returned */
+static khui_alert *
+alert_queue_peek(void) {
+ khui_alert * a;
+
+ if (is_alert_queue_empty())
+ return NULL;
+
+ a = alert_queue[alert_queue_head];
+ khui_alert_hold(a);
+
+ return a;
}
+/**********************************************************************
+ Alert List
-/* forward dcls */
-static khm_int32
-alert_show(khui_alert * a);
+ A list of alerts. Currently has a fixed upper limit, but the limit
+ is high enough for now.
+***********************************************************************/
-static khm_int32
-alert_show_minimized(khui_alert * a);
+typedef struct tag_alert_list {
+ khui_alert * alerts[KHUI_ALERT_QUEUE_MAX];
+ int n_alerts;
+ wchar_t title[KHUI_MAXCCH_TITLE];
+} alert_list;
-static khm_int32
-alert_show_normal(khui_alert * a);
+static void
+alert_list_init(alert_list * alist) {
+ ZeroMemory(alist, sizeof(*alist));
+}
+
+static void
+alert_list_set_title(alert_list * alist, wchar_t * title) {
+ StringCbCopy(alist->title, sizeof(alist->title), title);
+}
static khm_int32
-alert_enqueue(khui_alert * a);
+alert_list_add_alert(alert_list * alist,
+ khui_alert * alert) {
+
+ if (alist->n_alerts == ARRAYLENGTH(alist->alerts))
+ return KHM_ERROR_NO_RESOURCES;
+
+ khui_alert_hold(alert);
+ alist->alerts[alist->n_alerts++] = alert;
+
+ return KHM_ERROR_SUCCESS;
+}
+
+static void
+alert_list_destroy(alert_list * alist) {
+ int i;
+
+ for (i=0; i < alist->n_alerts; i++) {
+ if (alist->alerts[i] != NULL) {
+ khui_alert_release(alist->alerts[i]);
+ alist->alerts[i] = NULL;
+ }
+ }
+
+ alist->n_alerts = 0;
+}
+
+
+/**********************************************************************
+ Notifier Window
+
+ The notifier window manages the notification icon and handles
+ KMSG_ALERT messages sent from the UI library. The window will exist
+ for the lifetime of the application.
+***********************************************************************/
/* These are defined for APPVER >= 0x501. We are defining them here
so that we can build with APPVER = 0x500 and use the same binaries
@@ -156,15 +318,6 @@ alert_enqueue(khui_alert * a); #endif
-/**********************************************************************
- Notifier
-***********************************************************************
-
-The notifier is a message only window that listens for notifier
-messages. This window will exist for the lifetime of the application
-and will use alerter windows as needed to show application alerts.
-*/
-
static LRESULT CALLBACK
notifier_wnd_proc(HWND hwnd,
UINT uMsg,
@@ -182,13 +335,29 @@ notifier_wnd_proc(HWND hwnd, /* handle notifier messages */
switch(m->subtype) {
case KMSG_ALERT_SHOW:
- rv = alert_show((khui_alert *) m->vparam);
- khui_alert_release((khui_alert *) m->vparam);
+ {
+ khui_alert * a;
+
+ a = (khui_alert *) m->vparam;
+#ifdef DEBUG
+ assert(a != NULL);
+#endif
+ rv = alert_show(a);
+ khui_alert_release(a);
+ }
break;
case KMSG_ALERT_QUEUE:
- rv = alert_enqueue((khui_alert *) m->vparam);
- khui_alert_release((khui_alert *) m->vparam);
+ {
+ khui_alert * a;
+
+ a = (khui_alert *) m->vparam;
+#ifdef DEBUG
+ assert(a != NULL);
+#endif
+ rv = alert_enqueue(a);
+ khui_alert_release(a);
+ }
break;
case KMSG_ALERT_CHECK_QUEUE:
@@ -196,16 +365,7 @@ notifier_wnd_proc(HWND hwnd, break;
case KMSG_ALERT_SHOW_QUEUED:
- if (current_alert == NULL) {
- khui_alert * a;
-
- a = del_from_alert_queue();
- if (a) {
- rv = alert_show(a);
- check_for_queued_alerts();
- khui_alert_release(a);
- }
- }
+ show_queued_alerts();
break;
case KMSG_ALERT_SHOW_MODAL:
@@ -213,15 +373,20 @@ notifier_wnd_proc(HWND hwnd, khui_alert * a;
a = (khui_alert *) m->vparam;
+#ifdef DEBUG
+ assert(a != NULL);
+#endif
+ khui_alert_lock(a);
a->flags |= KHUI_ALERT_FLAG_MODAL;
+ khui_alert_unlock(a);
+
rv = alert_show(a);
- khui_alert_release(a);
if (KHM_SUCCEEDED(rv)) {
- notifier_modal_loop = TRUE;
-
- khm_message_loop_int(¬ifier_modal_loop);
+ khm_message_loop_int(&a->displayed);
}
+
+ khui_alert_release(a);
}
break;
}
@@ -246,56 +411,120 @@ notifier_wnd_proc(HWND hwnd, {
POINT pt;
int menu_id;
+ khui_menu_def * mdef;
+ khui_action_ref * act;
+ khm_size i, n;
+ khm_int32 def_cmd;
- GetCursorPos(&pt);
+ /* before we show the context menu, we need to make
+ sure that the default action for the notification
+ icon is present in the menu and that it is marked
+ as the default. */
+
+ def_cmd = get_default_notifier_action();
- if (khm_is_main_window_visible())
+ if (khm_is_main_window_visible()) {
menu_id = KHUI_MENU_ICO_CTX_NORMAL;
- else
+
+ if (def_cmd == KHUI_ACTION_OPEN_APP)
+ def_cmd = KHUI_ACTION_CLOSE_APP;
+ } else {
menu_id = KHUI_MENU_ICO_CTX_MIN;
+ }
+
+ mdef = khui_find_menu(menu_id);
+
+#ifdef DEBUG
+ assert(mdef);
+#endif
+ n = khui_menu_get_size(mdef);
+ for (i=0; i < n; i++) {
+ act = khui_menu_get_action(mdef, i);
+ if (!(act->flags & KHUI_ACTIONREF_PACTION) &&
+ (act->action == def_cmd))
+ break;
+ }
+
+ if (i < n) {
+ if (!(act->flags & KHUI_ACTIONREF_DEFAULT)) {
+ khui_menu_remove_action(mdef, i);
+ khui_menu_insert_action(mdef, i, def_cmd, KHUI_ACTIONREF_DEFAULT);
+ } else {
+ /* we are all set */
+ }
+ } else {
+ /* the default action was not found on the context
+ menu */
+#ifdef DEBUG
+ assert(FALSE);
+#endif
+ khui_menu_insert_action(mdef, 0, def_cmd, KHUI_ACTIONREF_DEFAULT);
+ }
SetForegroundWindow(khm_hwnd_main);
+ GetCursorPos(&pt);
khm_menu_show_panel(menu_id, pt.x, pt.y);
PostMessage(khm_hwnd_main, WM_NULL, 0, 0);
}
break;
- case WM_LBUTTONDOWN:
- /* we actually wait for the WM_LBUTTONUP before doing
- anything */
- break;
-
- case WM_LBUTTONUP:
- /* fall through */
case NIN_SELECT:
/* fall through */
case NIN_KEYSELECT:
- khm_show_main_window();
+ /* If there were any alerts waiting to be shown, we show
+ them. Otherwise we perform the default action. */
+ khm_notify_icon_activate();
break;
case NIN_BALLOONUSERCLICK:
- if (current_alert) {
- if ((current_alert->flags & KHUI_ALERT_FLAG_DEFACTION) &&
- current_alert->n_alert_commands > 0) {
+ if (balloon_alert) {
+ khui_alert * a;
+
+ khm_notify_icon_change(KHERR_NONE);
+
+ a = balloon_alert;
+ balloon_alert = NULL;
+
+ khui_alert_lock(a);
+ a->displayed = FALSE;
+
+ if ((a->flags & KHUI_ALERT_FLAG_DEFACTION) &&
+ !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) &&
+ a->n_alert_commands > 0) {
PostMessage(khm_hwnd_main, WM_COMMAND,
- MAKEWPARAM(current_alert->alert_commands[0],
+ MAKEWPARAM(a->alert_commands[0],
0),
0);
- } else if (current_alert->flags &
+ } else if (a->flags &
KHUI_ALERT_FLAG_REQUEST_WINDOW) {
khm_show_main_window();
- alert_show_normal(current_alert);
+ alert_show_normal(a);
}
+
+ khui_alert_unlock(a);
+ khui_alert_release(a);
+ } else {
+#ifdef DEBUG
+ assert(FALSE);
+#endif
}
- /* fallthrough */
+ break;
+
case NIN_BALLOONHIDE:
case NIN_BALLOONTIMEOUT:
khm_notify_icon_change(KHERR_NONE);
- if (current_alert) {
- khui_alert_release(current_alert);
- current_alert = NULL;
+ if (balloon_alert) {
+ khui_alert * a;
+ a = balloon_alert;
+ balloon_alert = NULL;
+
+ khui_alert_lock(a);
+ a->displayed = FALSE;
+ khui_alert_unlock(a);
+
+ khui_alert_release(a);
}
break;
}
@@ -342,85 +571,1431 @@ khm_register_notifier_wnd_class(void) Alerter
**********************************************************************/
-typedef struct tag_alerter_wnd_data {
+typedef struct tag_alerter_alert_data {
khui_alert * alert;
+ BOOL seen; /* has the user seen this alert? */
+
+ BOOL has_commands; /* we cache the value here. otherwise
+ we'll have to get a lock on the
+ alert each time we have to find out
+ whether there are any commands for
+ this alert. */
+
+ RECT r_alert; /* the entire alert, relative to self. */
+
+ /* the following rects are relative to the top left of r_alert. */
+
+ RECT r_title; /* the title. deflate by padding to
+ get the text rect. */
+ RECT r_icon; /* rect for icon */
+ RECT r_message; /* rect for the text. no padding
+ necessary. */
+ RECT r_suggestion; /* rect for the suggestion. deflate
+ by padding to get the suggestion
+ rect. The suggestion rect includes
+ space for the small icon on the
+ left and padding between the icon
+ and the text. The size of the small
+ icon are as per system metrics
+ SM_C{X,Y}SMICON. Padding is
+ s_pad.cx vertical. */
+
+ int n_cmd_buttons; /* number of command buttons in this alert. */
+
+ RECT r_buttons[KHUI_MAX_ALERT_COMMANDS];
+ /* rects for the command buttons. */
+
+ HWND hwnd_buttons[KHUI_MAX_ALERT_COMMANDS];
+ /* handles for the command buttons */
+
+ HWND hwnd_marker;
+ /* handle to the marker window used as
+ a tab-stop target when there are
+ not buttons associated with the
+ alert. */
+
+ LDCL(struct tag_alerter_alert_data);
+} alerter_alert_data;
+
+typedef struct tag_alerter_wnd_data {
HWND hwnd;
HFONT hfont;
- BOOL metrics_done;
+ wchar_t caption[KHUI_MAXCCH_TITLE]; /* the original
+ caption for the
+ dialog. */
- HWND hwnd_buttons[KHUI_MAX_ALERT_COMMANDS];
+ HWND hw_bin;
+ HWND hw_scroll;
+ HWND hw_close;
- /* various metrics */
+ int scroll_top;
- /* calculated during WM_CREATE and adjusted during WM_PAINT */
- int dy_message;
- int dy_suggestion;
+ int n_cmd_buttons; /* total number of command buttons
+ in all the alerts being shown in
+ this dialog. */
+ int c_alert; /* current selected alert. */
+
+ /* various metrics */
/* calculated during WM_CREATE */
- int dx_button;
- int dy_button;
- int dx_button_incr;
- int dx_margin;
- int dy_margin;
- int dy_bb;
- int x_message;
- int dx_message;
- int dx_icon;
- int dy_icon;
- int dx_suggest_pad;
-
- /* calculated during WM_CREATE and adjusted during WM_PAINT */
- int dx_client;
- int dy_client;
-
- /* calculated during WM_PAINT */
- int y_message;
- int y_suggestion;
-
- LDCL(struct tag_alerter_wnd_data);
-} alerter_wnd_data;
+ SIZE s_button; /* minimum dimensions for command button */
+ SIZE s_margin;
+ RECT r_text; /* only .left and .right are used. rest are 0 */
+ RECT r_title; /* only .left, .right and .bottom are used. .top=0 */
+ SIZE s_icon;
+ SIZE s_pad;
+
+ int cx_wnd;
+ int cy_max_wnd;
+
+ /* derived from the alert sizes */
+ SIZE s_alerts;
+
+ QDCL(alerter_alert_data); /* queue of alerts that are being
+ shown in this window. */
-alerter_wnd_data * khui_alerts = NULL;
+ LDCL(struct tag_alerter_wnd_data); /* for adding to
+ khui_alert_windows list. */
+
+ int n_alerts;
+
+} alerter_wnd_data;
#define NTF_PARAM DWLP_USER
/* dialog sizes in base dialog units */
-#define NTF_MARGIN 5
-#define NTF_WIDTH 200
+#define NTF_MARGIN 5
+#define NTF_WIDTH 200
+#define NTF_MAXHEIGHT 150
-#define NTF_BB_HEIGHT 15
+#define NTF_TITLE_X NTF_MARGIN
+#define NTF_TITLE_WIDTH (NTF_WIDTH - NTF_MARGIN*2)
+#define NTF_TITLE_HEIGHT 10
-#define NTF_ICON_X NTF_MARGIN
-#define NTF_ICON_WIDTH 20
-#define NTF_ICON_HEIGHT 20
+#define NTF_TEXT_PAD 2
-#define NTF_MSG_X (NTF_ICON_X + NTF_ICON_WIDTH + NTF_MARGIN)
-#define NTF_MSG_WIDTH ((NTF_WIDTH - NTF_MARGIN) - NTF_MSG_X)
-#define NTF_MSG_HEIGHT 15
+#define NTF_BUTTON_HEIGHT 14
-#define NTF_SUG_X NTF_MSG_X
-#define NTF_SUG_WIDTH NTF_MSG_WIDTH
-#define NTF_SUG_HEIGHT NTF_MSG_HEIGHT
-#define NTF_SUG_PAD 2
+#define NTF_TIMEOUT 20000
-#define NTF_BUTTON_X NTF_MSG_X
+#define ALERT_WINDOW_EX_SYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP)
+#define ALERT_WINDOW_STYLES (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN | DS_NOIDLEMSG)
-#define NTF_BUTTON_WIDTH ((NTF_MSG_WIDTH - 3*NTF_MARGIN) / 4)
-#define NTF_BUTTON_XINCR (NTF_BUTTON_WIDTH + NTF_MARGIN)
-#define NTF_BUTTON_HEIGHT (NTF_BB_HEIGHT - NTF_MARGIN)
+/* Control ids */
+#define IDC_NTF_ALERTBIN 998
+#define IDC_NTF_CLOSE 999
-#define NTF_TIMEOUT 20000
+#define IDC_NTF_CMDBUTTONS 1001
+#define IDC_FROM_IDX(alert, bn) ((alert) * (KHUI_MAX_ALERT_COMMANDS + 1) + (bn) + 1 + IDC_NTF_CMDBUTTONS)
+#define ALERT_FROM_IDC(idc) (((idc) - IDC_NTF_CMDBUTTONS) / (KHUI_MAX_ALERT_COMMANDS + 1))
+#define BUTTON_FROM_IDC(idc) (((idc) - IDC_NTF_CMDBUTTONS) % (KHUI_MAX_ALERT_COMMANDS + 1) - 1)
+
+/* if the only command in an alert is "Close", we assume that the
+ alert has no commands. */
+#define ALERT_HAS_CMDS(a) ((a)->n_alert_commands > 1 || ((a)->n_alert_commands == 1 && (a)->alert_commands[0] != KHUI_PACTION_CLOSE))
+
+#define SCROLL_LINE_SIZE(d) ((d)->cy_max_wnd / 12)
+
+static void
+add_alert_to_wnd_data(alerter_wnd_data * d,
+ khui_alert * a) {
+ alerter_alert_data * aiter;
+ khm_boolean exists = 0;
+
+ khui_alert_lock(a);
+
+ /* check if the alert is already there */
+ aiter = QTOP(d);
+ while(aiter && !exists) {
+ if (aiter->alert) {
+ khui_alert_lock(aiter->alert);
+
+ if (alert_is_equal(aiter->alert, a)) {
+ exists = TRUE;
+ }
+
+ khui_alert_unlock(aiter->alert);
+ }
+
+ aiter = QNEXT(aiter);
+ }
+
+ a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW;
+
+ if (!exists) {
+ a->displayed = TRUE;
+ }
+
+ khui_alert_unlock(a);
+
+ if (!exists) {
+ alerter_alert_data * adata;
+
+ adata = PMALLOC(sizeof(*adata));
+ ZeroMemory(adata, sizeof(*adata));
+
+ adata->alert = a;
+ khui_alert_hold(a);
+
+ QPUT(d, adata);
+ d->n_alerts ++;
+ }
+}
+
+static alerter_wnd_data *
+create_alerter_wnd_data(HWND hwnd, alert_list * l) {
+ alerter_wnd_data * d;
+ int i;
+ LONG dlgb;
+
+ d = PMALLOC(sizeof(*d));
+ ZeroMemory(d, sizeof(*d));
+
+ d->hwnd = hwnd;
+
+ GetWindowText(hwnd, d->caption, ARRAYLENGTH(d->caption));
+
+ for (i=0; i < l->n_alerts; i++) {
+ add_alert_to_wnd_data(d, l->alerts[i]);
+ }
+
+ d->n_alerts = l->n_alerts;
+
+ LPUSH(&khui_alert_windows, d);
+
+ /* Compute a few metrics first */
+
+ dlgb = GetDialogBaseUnits();
+
+#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4)
+#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8)
+
+ d->cx_wnd = DLG2SCNX(NTF_WIDTH);
+ d->cy_max_wnd = DLG2SCNY(NTF_MAXHEIGHT);
+
+ d->s_margin.cx = DLG2SCNX(NTF_MARGIN);
+ d->s_margin.cy = DLG2SCNY(NTF_MARGIN);
+
+ d->r_title.left = DLG2SCNX(NTF_TITLE_X);
+ d->r_title.right = DLG2SCNX(NTF_TITLE_X + NTF_TITLE_WIDTH);
+ d->r_title.top = 0;
+ d->r_title.bottom = DLG2SCNY(NTF_TITLE_HEIGHT);
+
+ d->s_pad.cx = DLG2SCNX(NTF_TEXT_PAD);
+ d->s_pad.cy = DLG2SCNY(NTF_TEXT_PAD);
+
+ d->s_icon.cx = GetSystemMetrics(SM_CXICON);
+ d->s_icon.cy = GetSystemMetrics(SM_CYICON);
+
+ d->r_text.left = d->s_margin.cx * 2 + d->s_icon.cx;
+ d->r_text.right = d->cx_wnd - d->s_margin.cx;
+ d->r_text.top = 0;
+ d->r_text.bottom = 0;
+
+ d->s_button.cx = ((d->r_text.right - d->r_text.left) - (KHUI_MAX_ALERT_COMMANDS - 1) * d->s_margin.cx) / KHUI_MAX_ALERT_COMMANDS;
+ d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT);
+
+#undef DLG2SCNX
+#undef DLG2SCNY
+
+ d->c_alert = -1;
+
+ return d;
+}
+
+static void
+layout_alert(HDC hdc, alerter_wnd_data * d,
+ alerter_alert_data * adata) {
+ RECT r;
+ size_t len;
+ int y;
+ int icon_y;
+
+#ifdef DEBUG
+ assert(adata->alert);
+#endif
+
+ khui_alert_lock(adata->alert);
+
+ y = 0;
+
+ /* Title */
+
+ y += d->s_margin.cy;
+
+ /* If there is a title and it differs from the title of the
+ alerter window, then we have to show the alert title
+ separately. */
+ if (adata->alert->title &&
+ wcscmp(adata->alert->title, d->caption)) {
+
+ CopyRect(&adata->r_title, &d->r_title);
+ OffsetRect(&adata->r_title, 0, y);
+
+ y = adata->r_title.bottom + d->s_margin.cy;
+
+ } else {
+
+ SetRectEmpty(&adata->r_title);
+
+ }
+
+ /* Icon */
+
+ SetRect(&adata->r_icon, d->s_margin.cx, y,
+ d->s_margin.cx + d->s_icon.cx,
+ y + d->s_icon.cy);
+
+ icon_y = adata->r_icon.bottom + d->s_margin.cy; /* the bottom of the icon */
+
+ /* Message */
+
+ if (adata->alert->message &&
+ SUCCEEDED(StringCchLength(adata->alert->message,
+ KHUI_MAXCCH_MESSAGE,
+ &len))) {
+
+ CopyRect(&r, &d->r_text);
+
+ DrawTextEx(hdc, adata->alert->message, (int) len,
+ &r,
+ DRAWTEXTOPTIONS,
+ NULL);
+
+ OffsetRect(&r, 0, y);
+ CopyRect(&adata->r_message, &r);
+
+ y = r.bottom + d->s_margin.cy;
+
+ } else {
+
+ SetRectEmpty(&adata->r_message);
+
+ }
+
+ /* Suggestion */
+
+ if (adata->alert->suggestion &&
+ SUCCEEDED(StringCchLength(adata->alert->suggestion,
+ KHUI_MAXCCH_SUGGESTION,
+ &len))) {
+ int pad = d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);
+
+ CopyRect(&r, &d->r_text);
+ r.left += pad;
+
+ DrawTextEx(hdc, adata->alert->suggestion, (int) len,
+ &r,
+ DRAWTEXTOPTIONS,
+ NULL);
+
+ r.left -= pad;
+
+ InflateRect(&r, d->s_pad.cx, d->s_pad.cy);
+ OffsetRect(&r, 0, -r.top + y);
+ CopyRect(&adata->r_suggestion, &r);
+
+ y = r.bottom + d->s_margin.cy;
+
+ } else {
+
+ SetRectEmpty(&adata->r_suggestion);
+
+ }
+
+ y = max(y, icon_y);
+
+ /* Buttons */
+
+ if (ALERT_HAS_CMDS(adata->alert)) {
+ khm_int32 i;
+ int x, width;
+ wchar_t caption[KHUI_MAXCCH_SHORT_DESC];
+ size_t len;
+ SIZE s;
+ int skip_close;
+
+ adata->has_commands = TRUE;
+
+ if (d->n_alerts > 1)
+ skip_close = TRUE;
+ else
+ skip_close = FALSE;
+
+ x = d->r_text.left;
+
+#ifdef DEBUG
+ assert(adata->alert->n_alert_commands <= KHUI_MAX_ALERT_COMMANDS);
+#endif
+
+ for (i=0; i < adata->alert->n_alert_commands; i++) {
+
+ if (adata->alert->alert_commands[i] == KHUI_PACTION_CLOSE && skip_close) {
+ SetRectEmpty(&adata->r_buttons[i]);
+ continue;
+ }
+
+ caption[0] = L'\0';
+ len = 0;
+ khm_get_action_caption(adata->alert->alert_commands[i],
+ caption, sizeof(caption));
+ StringCchLength(caption, ARRAYLENGTH(caption), &len);
+
+ if (!GetTextExtentPoint32(hdc, caption, (int) len, &s)) {
+ width = d->s_button.cx;
+ } else {
+ width = s.cx + d->s_margin.cx * 2;
+ }
+
+ if (width < d->s_button.cx)
+ width = d->s_button.cx;
+ else if (width > (d->r_text.right - d->r_text.left))
+ width = d->r_text.right - d->r_text.left;
+
+ if (x + width > d->r_text.right) {
+ /* new line */
+ x = d->r_text.left;
+ y += d->s_button.cy + d->s_pad.cy;
+ }
+
+ SetRect(&adata->r_buttons[i], x, y, x + width, y + d->s_button.cy);
+
+ x += width + d->s_margin.cx;
+ }
+
+ y += d->s_button.cy + d->s_margin.cy;
+ }
+
+ khui_alert_unlock(adata->alert);
+
+ /* Now set the rect for the whole alert */
+ SetRect(&adata->r_alert, 0, 0, d->cx_wnd, y);
+
+}
+
+static void
+pick_title_for_alerter_window(alerter_wnd_data * d) {
+ alerter_alert_data * adata;
+ wchar_t caption[KHUI_MAXCCH_TITLE];
+ khm_boolean common_caption = TRUE;
+ khui_alert_type ctype = KHUI_ALERTTYPE_NONE;
+ khm_boolean common_type = TRUE;
+
+ /* - If all the alerts have the same title, then we use the common
+ title.
+
+ - If all the alerts are of the same type, then we pick a title
+ that is suitable for the type.
+
+ - All else fails, we use a default caption for the window.
+ */
+
+ caption[0] = L'\0';
+ adata = QTOP(d);
+ while (adata && (common_caption || common_type)) {
+
+ if (adata->alert) {
+ khui_alert_lock(adata->alert);
+
+ if (common_caption) {
+ if (caption[0] == L'\0') {
+ if (adata->alert->title)
+ StringCbCopy(caption, sizeof(caption), adata->alert->title);
+ } else if (adata->alert->title &&
+ wcscmp(caption, adata->alert->title)) {
+ common_caption = FALSE;
+ }
+ }
+
+ if (common_type) {
+ if (ctype == KHUI_ALERTTYPE_NONE)
+ ctype = adata->alert->alert_type;
+ else if (ctype != adata->alert->alert_type)
+ common_type = FALSE;
+ }
+
+ khui_alert_unlock(adata->alert);
+ }
+
+ adata = QNEXT(adata);
+ }
+
+ /* just in case someone changes d->caption to a pointer from an
+ array */
+#ifdef DEBUG
+ assert(sizeof(d->caption) > sizeof(wchar_t *));
+#endif
+
+ if (common_caption && caption[0] != L'\0') {
+ StringCbCopy(d->caption, sizeof(d->caption), caption);
+ } else if (common_type && ctype != KHUI_ALERTTYPE_NONE) {
+ switch(ctype) {
+ case KHUI_ALERTTYPE_PLUGIN:
+ LoadString(khm_hInstance, IDS_ALERTTYPE_PLUGIN,
+ d->caption, ARRAYLENGTH(d->caption));
+ break;
+
+ case KHUI_ALERTTYPE_EXPIRE:
+ LoadString(khm_hInstance, IDS_ALERTTYPE_EXPIRE,
+ d->caption, ARRAYLENGTH(d->caption));
+ break;
+
+ case KHUI_ALERTTYPE_RENEWFAIL:
+ LoadString(khm_hInstance, IDS_ALERTTYPE_RENEWFAIL,
+ d->caption, ARRAYLENGTH(d->caption));
+ break;
+
+ case KHUI_ALERTTYPE_ACQUIREFAIL:
+ LoadString(khm_hInstance, IDS_ALERTTYPE_ACQUIREFAIL,
+ d->caption, ARRAYLENGTH(d->caption));
+ break;
+
+ case KHUI_ALERTTYPE_CHPW:
+ LoadString(khm_hInstance, IDS_ALERTTYPE_CHPW,
+ d->caption, ARRAYLENGTH(d->caption));
+ break;
+
+ default:
+ LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+ d->caption, ARRAYLENGTH(d->caption));
+ }
+ } else {
+ LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+ d->caption, ARRAYLENGTH(d->caption));
+ }
+
+ SetWindowText(d->hwnd, d->caption);
+}
+
+static void
+estimate_alerter_wnd_sizes(alerter_wnd_data * d) {
+ HDC hdc;
+ HFONT hf_old;
+ int height = 0;
+
+ alerter_alert_data * adata;
+
+ pick_title_for_alerter_window(d);
+
+ hdc = GetDC(d->hwnd);
+#ifdef DEBUG
+ assert(hdc);
+#endif
+
+ if (d->hfont == NULL)
+ d->hfont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
+
+#ifdef DEBUG
+ assert(d->hfont);
+#endif
+
+ hf_old = SelectFont(hdc, d->hfont);
+
+ adata = QTOP(d);
+ while(adata) {
+ layout_alert(hdc, d, adata);
+
+ height += adata->r_alert.bottom;
+
+ adata = QNEXT(adata);
+ }
+
+ SelectFont(hdc, hf_old);
+ ReleaseDC(d->hwnd, hdc);
+
+ d->s_alerts.cx = d->cx_wnd;
+ d->s_alerts.cy = height;
+}
+
+static void
+layout_command_buttons(alerter_wnd_data * d) {
+
+ alerter_alert_data * adata;
+ HDWP hdefer;
+ int y;
+
+ hdefer = BeginDeferWindowPos(d->n_cmd_buttons);
+
+ y = 0;
+ adata = QTOP(d);
+ while (adata) {
+ RECT r;
+ int i;
+
+ if (!adata->has_commands)
+ goto done;
+
+ for (i=0; i < adata->n_cmd_buttons; i++) {
+ if (IsRectEmpty(&adata->r_buttons[i])) {
+ /* the button is no longer needed */
+ if (adata->hwnd_buttons[i] != NULL) {
+ DestroyWindow(adata->hwnd_buttons[i]);
+ adata->hwnd_buttons[i] = NULL;
+ }
+
+ continue;
+ }
+
+ if (adata->hwnd_buttons[i] == NULL) {
+ continue;
+ }
+
+ CopyRect(&r, &adata->r_buttons[i]);
+ OffsetRect(&r, 0, y - d->scroll_top);
+
+ DeferWindowPos(hdefer,
+ adata->hwnd_buttons[i], NULL,
+ r.left, r.top, 0, 0,
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |
+ SWP_NOSIZE);
+ }
+
+ done:
+ y += adata->r_alert.bottom;
+ adata = QNEXT(adata);
+ }
+
+ EndDeferWindowPos(hdefer);
+}
+
+static void
+setup_alerter_window_controls(alerter_wnd_data * d) {
+
+ RECT r_alerts;
+ RECT r_window;
+ RECT r_client;
+ RECT r_parent;
+ HWND hw_parent;
+ HWND hw_focus = NULL;
+ BOOL close_button = FALSE;
+ BOOL scrollbar = FALSE;
+ BOOL redraw_scollbar = FALSE;
+
+ /* estimate_alerter_wnd_sizes() must be called before calling
+ this. */
+#ifdef DEBUG
+ assert(d->s_alerts.cy > 0);
+#endif
+
+ r_alerts.left = 0;
+ r_alerts.top = 0;
+ r_alerts.right = d->cx_wnd;
+
+ if (d->s_alerts.cy > d->cy_max_wnd) {
+
+ BOOL redraw = FALSE;
+
+ r_alerts.right += GetSystemMetrics(SM_CXVSCROLL);
+ r_alerts.bottom = d->cy_max_wnd;
+
+ CopyRect(&r_client, &r_alerts);
+ r_client.bottom += d->s_margin.cy + d->s_button.cy + d->s_pad.cy;
+ close_button = TRUE;
+
+ if (d->scroll_top > d->s_alerts.cy - d->cy_max_wnd)
+ d->scroll_top = d->s_alerts.cy - d->cy_max_wnd;
+
+ scrollbar = TRUE;
+ } else {
+ r_alerts.bottom = d->s_alerts.cy;
+
+ CopyRect(&r_client, &r_alerts);
+
+ if (d->n_alerts == 1) {
+
+ if (!QTOP(d)->has_commands) {
+ r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;
+ close_button = TRUE;
+ }
+
+ } else {
+
+ r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;
+ close_button = TRUE;
+ }
+
+ d->scroll_top = 0;
+ }
+
+ if (d->hw_bin == NULL) {
+ d->hw_bin = CreateWindowEx(WS_EX_CONTROLPARENT,
+ MAKEINTATOM(atom_alert_bin),
+ L"Alert Container",
+ WS_CHILD | WS_CLIPCHILDREN |
+ WS_VISIBLE |
+ ((scrollbar)? WS_VSCROLL : 0),
+ r_alerts.left, r_alerts.top,
+ r_alerts.right - r_alerts.left,
+ r_alerts.bottom - r_alerts.top,
+ d->hwnd,
+ (HMENU) IDC_NTF_ALERTBIN,
+ khm_hInstance,
+ (LPVOID) d);
+ } else {
+ redraw_scollbar = TRUE;
+ SetWindowLongPtr(d->hw_bin, GWL_STYLE,
+ WS_CHILD | WS_CLIPCHILDREN |
+ WS_VISIBLE |
+ ((scrollbar)? WS_VSCROLL : 0));
+ SetWindowPos(d->hw_bin, NULL,
+ r_alerts.left, r_alerts.top,
+ r_alerts.right - r_alerts.left,
+ r_alerts.bottom - r_alerts.top,
+ SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+
+ if (scrollbar) {
+ SCROLLINFO si;
+
+ ZeroMemory(&si, sizeof(si));
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ si.nMin = 0;
+ si.nMax = d->s_alerts.cy;
+ si.nPage = d->cy_max_wnd;
+ si.nPos = d->scroll_top;
+
+ SetScrollInfo(d->hw_bin, SB_VERT, &si, redraw_scollbar);
+ }
+
+ /* create the action buttons */
+ {
+ alerter_alert_data * adata;
+ int y;
+ int idx;
+ HWND last_window = HWND_TOP;
+ int n_buttons = 0;
+
+ idx = 0;
+ y = - d->scroll_top;
+ adata = QTOP(d);
+ while(adata) {
+ if (adata->has_commands) {
+ int i;
+ wchar_t caption[KHUI_MAXCCH_SHORT_DESC];
+ RECT r;
+
+ if (adata->hwnd_marker) {
+ DestroyWindow(adata->hwnd_marker);
+ adata->hwnd_marker = NULL;
+ }
+
+ khui_alert_lock(adata->alert);
+
+ adata->n_cmd_buttons = adata->alert->n_alert_commands;
+
+ for (i=0; i < adata->alert->n_alert_commands; i++) {
+
+ n_buttons ++;
+
+ if (IsRectEmpty(&adata->r_buttons[i])) {
+ /* this button is not necessary */
+ if (adata->hwnd_buttons[i]) {
+ DestroyWindow(adata->hwnd_buttons[i]);
+ adata->hwnd_buttons[i] = NULL;
+ }
+
+ continue;
+ }
+
+ if (adata->hwnd_buttons[i] != NULL) {
+ /* already there */
+ CopyRect(&r, &adata->r_buttons[i]);
+ OffsetRect(&r, 0, y);
+
+ SetWindowPos(adata->hwnd_buttons[i], last_window,
+ r.left, r.top,
+ r.right - r.left,
+ r.bottom - r.top,
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER |
+ SWP_SHOWWINDOW);
+
+ last_window = adata->hwnd_buttons[i];
+
+ if (hw_focus == NULL)
+ hw_focus = adata->hwnd_buttons[i];
+
+ continue;
+ }
+
+ khm_get_action_caption(adata->alert->alert_commands[i],
+ caption, sizeof(caption));
+
+ CopyRect(&r, &adata->r_buttons[i]);
+ OffsetRect(&r, 0, y);
+
+ adata->hwnd_buttons[i] =
+ CreateWindowEx(0,
+ L"BUTTON",
+ caption,
+ WS_CHILD | WS_TABSTOP | BS_NOTIFY,
+ r.left, r.top,
+ r.right - r.left,
+ r.bottom - r.top,
+ d->hw_bin,
+ (HMENU) (INT_PTR) IDC_FROM_IDX(idx, i),
+ khm_hInstance,
+ NULL);
+#ifdef DEBUG
+ assert(adata->hwnd_buttons[i]);
+#endif
+
+ if (d->hfont) {
+ SendMessage(adata->hwnd_buttons[i], WM_SETFONT,
+ (WPARAM) d->hfont, FALSE);
+ }
+
+ SetWindowPos(adata->hwnd_buttons[i], last_window,
+ 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+ last_window = adata->hwnd_buttons[i];
+
+ if (hw_focus == NULL)
+ hw_focus = adata->hwnd_buttons[i];
+ }
+
+ khui_alert_unlock(adata->alert);
+ } else {
+ int i;
+
+ /* Destroy any buttons that belong to the alert. We
+ might have some left over, if there were command
+ belonging to the alert that were ignored.*/
+
+ for (i=0; i < adata->n_cmd_buttons; i++) {
+ if (adata->hwnd_buttons[i]) {
+ DestroyWindow(adata->hwnd_buttons[i]);
+ adata->hwnd_buttons[i] = NULL;
+ }
+ }
+
+ adata->n_cmd_buttons = 0;
+
+ if (adata->hwnd_marker == NULL) {
+ adata->hwnd_marker =
+ CreateWindowEx(0,
+ L"BUTTON",
+ L"Marker",
+ WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_NOTIFY,
+ -10, 0,
+ 5, 5,
+ d->hw_bin,
+ (HMENU) (INT_PTR) IDC_FROM_IDX(idx, -1),
+ khm_hInstance,
+ NULL);
+#ifdef DEBUG
+ assert(adata->hwnd_marker);
+#endif
+ }
+
+ SetWindowPos(adata->hwnd_marker, last_window,
+ 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER |
+ SWP_NOMOVE | SWP_NOSIZE);
+
+ last_window = adata->hwnd_marker;
+
+ if (scrollbar) {
+ EnableWindow(adata->hwnd_marker, TRUE);
+ if (hw_focus == NULL)
+ hw_focus = adata->hwnd_marker;
+ } else {
+ EnableWindow(adata->hwnd_marker, FALSE);
+ }
+ }
+
+ y += adata->r_alert.bottom;
+ adata = QNEXT(adata);
+ idx++;
+ }
+
+ d->n_cmd_buttons = n_buttons;
+ }
+
+ if (close_button) {
+ if (d->hw_close == NULL) {
+ wchar_t caption[256];
+
+ khm_get_action_caption(KHUI_PACTION_CLOSE, caption, sizeof(caption));
+
+ d->hw_close = CreateWindowEx(0,
+ L"BUTTON",
+ caption,
+ WS_CHILD | BS_DEFPUSHBUTTON | WS_TABSTOP | BS_NOTIFY,
+ 0,0,100,100,
+ d->hwnd,
+ (HMENU) IDC_NTF_CLOSE,
+ khm_hInstance,
+ NULL);
+
+#ifdef DEBUG
+ assert(d->hw_close);
+ assert(d->hfont);
+#endif
+ if (d->hfont)
+ SendMessage(d->hw_close, WM_SETFONT, (WPARAM) d->hfont, FALSE);
+ }
+
+ {
+ int x,y,width,height;
+
+ x = d->r_text.left;
+ y = r_client.bottom - (d->s_margin.cy + d->s_button.cy);
+ width = d->s_button.cx;
+ height = d->s_button.cy;
+
+ SetWindowPos(d->hw_close, NULL,
+ x, y, width, height,
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |
+ SWP_SHOWWINDOW);
+ }
+
+ if (hw_focus == NULL || d->n_cmd_buttons == 0)
+ hw_focus = d->hw_close;
+
+ } else {
+ if (d->hw_close != NULL) {
+ DestroyWindow(d->hw_close);
+ d->hw_close = NULL;
+ }
+ }
+
+ CopyRect(&r_window, &r_client);
+ AdjustWindowRectEx(&r_window, ALERT_WINDOW_STYLES,
+ FALSE, ALERT_WINDOW_EX_SYLES);
+ OffsetRect(&r_window, -r_window.left, -r_window.top);
+
+ /* center the window above the parent window. */
+
+ hw_parent = GetWindow(d->hwnd, GW_OWNER);
+ GetWindowRect(hw_parent, &r_parent);
+
+ {
+ int x,y;
+
+ x = (r_parent.left + r_parent.right - (r_window.right - r_window.left)) / 2;
+ y = (r_parent.top + r_parent.bottom - (r_window.bottom - r_window.top)) / 2;
+
+ SetWindowPos(d->hwnd,
+ HWND_TOP,
+ x, y,
+ r_window.right - r_window.left,
+ r_window.bottom - r_window.top,
+ SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
+ }
+
+ if (hw_focus != NULL)
+ PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE, 0));
+}
+
+static void
+scroll_to_position(alerter_wnd_data * d, int new_pos, khm_boolean redraw_scrollbar) {
+ int delta;
+ SCROLLINFO si;
+ HWND hwnd = d->hw_bin;
+
+ if (new_pos < 0)
+ new_pos = 0;
+ else if (new_pos > d->s_alerts.cy - d->cy_max_wnd)
+ new_pos = d->s_alerts.cy - d->cy_max_wnd;
+
+ if (new_pos == d->scroll_top)
+ return;
+
+ delta = d->scroll_top - new_pos;
+
+ d->scroll_top -= delta;
+
+ ScrollWindowEx(hwnd, 0, delta,
+ NULL, NULL, NULL, NULL,
+ SW_INVALIDATE | SW_ERASE);
+
+ layout_command_buttons(d);
+
+ ZeroMemory(&si, sizeof(si));
+
+ si.fMask = SIF_POS;
+ si.nPos = d->scroll_top;
+
+ SetScrollInfo(hwnd, SB_VERT, &si, redraw_scrollbar);
+}
+
+static void
+select_alert(alerter_wnd_data * d, int alert) {
+
+ int y;
+ RECT old_sel, new_sel;
+ alerter_alert_data * adata;
+ int idx;
+
+ if (d->n_alerts == 1 ||
+ alert < 0 ||
+ alert > d->n_alerts ||
+ d->c_alert == alert)
+ return;
+
+ SetRectEmpty(&old_sel);
+ SetRectEmpty(&new_sel);
+ idx = 0; y = -d->scroll_top;
+ adata = QTOP(d);
+ while(adata && (idx <= d->c_alert || idx <= alert)) {
+
+ if (idx == d->c_alert) {
+ CopyRect(&old_sel, &adata->r_alert);
+ OffsetRect(&old_sel, 0, y);
+ }
+
+ if (idx == alert) {
+ CopyRect(&new_sel, &adata->r_alert);
+ OffsetRect(&new_sel, 0, y);
+ }
+
+ y += adata->r_alert.bottom;
+ idx ++;
+ adata = QNEXT(adata);
+ }
+
+ d->c_alert = alert;
+ if (!IsRectEmpty(&old_sel))
+ InvalidateRect(d->hw_bin, &old_sel, TRUE);
+ if (!IsRectEmpty(&new_sel))
+ InvalidateRect(d->hw_bin, &new_sel, TRUE);
+}
+
+static void
+ensure_command_is_visible(alerter_wnd_data * d, int id) {
+ int alert_idx;
+ int y = 0;
+ alerter_alert_data * adata;
+ int new_pos = 0;
+
+ alert_idx = ALERT_FROM_IDC(id);
+
+#ifdef DEBUG
+ assert(alert_idx >= 0 && alert_idx < d->n_alerts);
+#endif
+ if (alert_idx >= d->n_alerts || alert_idx < 0)
+ return;
+
+ adata = QTOP(d);
+ while(adata && alert_idx > 0) {
+ y += adata->r_alert.bottom;
+ alert_idx--;
+ adata = QNEXT(adata);
+ }
+
+#ifdef DEBUG
+ assert(alert_idx == 0);
+ assert(adata);
+ assert(adata->alert);
+#endif
+ if (adata == NULL || alert_idx != 0)
+ return;
+
+ new_pos = d->scroll_top;
+ if (y < d->scroll_top) {
+ new_pos = y;
+ } else if (y + adata->r_alert.bottom > d->scroll_top + d->cy_max_wnd) {
+ new_pos = y + adata->r_alert.bottom - d->cy_max_wnd;
+ }
+
+ if (new_pos != d->scroll_top)
+ scroll_to_position(d, new_pos, TRUE);
+
+ select_alert(d, ALERT_FROM_IDC(id));
+}
+
+static void
+handle_mouse_select(alerter_wnd_data * d, int mouse_x, int mouse_y) {
+ int y;
+ alerter_alert_data * adata;
+
+ y = -d->scroll_top;
+ adata = QTOP(d);
+ while(adata) {
+ if (y <= mouse_y && (y + adata->r_alert.bottom) > mouse_y) {
+ HWND hw = NULL;
+
+ if (adata->n_cmd_buttons > 0)
+ hw = adata->hwnd_buttons[0];
+ else
+ hw = adata->hwnd_marker;
+
+ if (hw && !IsWindowEnabled(hw))
+ hw = GetNextDlgTabItem(d->hwnd, hw, FALSE);
+
+ if (hw)
+ PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw, MAKELPARAM(TRUE, 0));
+
+ return;
+ }
+
+ y += adata->r_alert.bottom;
+ adata = QNEXT(adata);
+ }
+}
+
+static void
+process_command_button(alerter_wnd_data * d, int id) {
+ int alert_idx;
+ int cmd_idx;
+ khm_int32 flags = 0;
+ khm_int32 cmd = 0;
+ alerter_alert_data * adata;
+ int i;
+
+ alert_idx = ALERT_FROM_IDC(id);
+ cmd_idx = BUTTON_FROM_IDC(id);
+
+#ifdef DEBUG
+ assert(alert_idx >= 0 && alert_idx < d->n_alerts);
+#endif
+ if (alert_idx >= d->n_alerts || alert_idx < 0)
+ return;
+
+ if (cmd_idx < 0) {
+ /* the user selected a marker button. Nothing to do. */
+ return;
+ }
+
+ adata = QTOP(d);
+ while(adata && alert_idx > 0) {
+ alert_idx--;
+ adata = QNEXT(adata);
+ }
+
+#ifdef DEBUG
+ assert(alert_idx == 0);
+ assert(adata);
+ assert(adata->alert);
+#endif
+ if (adata == NULL || alert_idx != 0)
+ return;
+
+ khui_alert_lock(adata->alert);
+#ifdef DEBUG
+ assert(cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands);
+#endif
+
+ if (cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands) {
+ cmd = adata->alert->alert_commands[cmd_idx];
+ }
+
+ flags = adata->alert->flags;
+
+ adata->alert->response = cmd;
+
+ khui_alert_unlock(adata->alert);
+
+ /* if we were supposed to dispatch the command, do so */
+ if (cmd != 0 &&
+ cmd != KHUI_PACTION_CLOSE &&
+ (flags & KHUI_ALERT_FLAG_DISPATCH_CMD)) {
+ PostMessage(khm_hwnd_main, WM_COMMAND,
+ MAKEWPARAM(cmd, 0), 0);
+ }
+
+ /* if this was the only alert in the alert group and its close
+ button was clicked, we close the alert window. Otherwise, the
+ alert window creates its own close button that closes the
+ window. */
+ if (d->n_alerts == 1) {
+ PostMessage(d->hwnd, WM_CLOSE, 0, 0);
+ }
+
+ /* While we are at it, we should disable the buttons for this
+ alert since we have already dispatched the command for it. */
+ if (cmd != 0) {
+ HWND hw_focus = GetFocus();
+ khm_boolean focus_trapped = FALSE;
+
+ for (i=0; i < adata->n_cmd_buttons; i++) {
+ if (adata->hwnd_buttons[i]) {
+ if (hw_focus == adata->hwnd_buttons[i])
+ focus_trapped = TRUE;
+
+ EnableWindow(adata->hwnd_buttons[i], FALSE);
+ }
+ }
+
+ if (focus_trapped) {
+ hw_focus = GetNextDlgTabItem(d->hwnd, hw_focus, FALSE);
+ if (hw_focus)
+ PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE,0));
+ }
+ }
+}
+
+static void
+destroy_alerter_wnd_data(alerter_wnd_data * d) {
+ alerter_alert_data * adata;
+
+ LDELETE(&khui_alert_windows, d);
+
+ QGET(d, &adata);
+ while(adata) {
+
+ if (adata->alert) {
+
+ khui_alert_lock(adata->alert);
+
+ adata->alert->displayed = FALSE;
+
+ khui_alert_unlock(adata->alert);
+
+ khui_alert_release(adata->alert);
+ adata->alert = NULL;
+ }
+
+ PFREE(adata);
+
+ QGET(d, &adata);
+ }
+
+ PFREE(d);
+}
+
+/* both ref and to_add must be locked and held */
+static khm_boolean
+alert_can_consolidate(khui_alert * ref,
+ khui_alert * to_add,
+ alert_list * alist) {
+
+ /* first check if we can add anything */
+ if (alist->n_alerts == ARRAYLENGTH(alist->alerts))
+ return FALSE;
+
+#ifdef DEBUG
+ assert(to_add != NULL);
+#endif
+
+ if (ref == NULL) {
+ /* we are testing whether to_add should be added to the alist
+ on its own. */
+ if ((to_add->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&
+ !(to_add->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW)) {
+ /* already displayed */
+ return FALSE;
+ }
+
+ if ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |
+ KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON) {
+ /* needs to be shown in a balloon */
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ /* if the ref or to_add are marked for modal, then we can't
+ consolidate them */
+ if ((ref->flags & KHUI_ALERT_FLAG_MODAL) ||
+ (to_add->flags & KHUI_ALERT_FLAG_MODAL))
+ return FALSE;
+
+ /* also, if either of them have requested to be exclusively shown
+ in a balloon, then we can't consolidate them. */
+ if (((ref->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |
+ KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON)
+
+ ||
+
+ ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |
+ KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON))
+ return FALSE;
+
+ /* for now, all we check if whether they are of the same type. */
+ if (ref->alert_type != KHUI_ALERTTYPE_NONE &&
+ ref->alert_type == to_add->alert_type)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* both a1 and a2 must be locked */
+static khm_boolean
+alert_is_equal(khui_alert * a1, khui_alert * a2) {
+ khm_int32 i;
+
+ if ((a1->severity != a2->severity) ||
+ (a1->n_alert_commands != a2->n_alert_commands) ||
+ (a1->title && (!a2->title || wcscmp(a1->title, a2->title))) ||
+ (!a1->title && a2->title) ||
+ (a1->message && (!a2->message || wcscmp(a1->message, a2->message))) ||
+ (!a1->message && a2->message) ||
+ (a1->suggestion && (!a2->suggestion || wcscmp(a1->suggestion, a2->suggestion))) ||
+ (!a1->suggestion && a2->suggestion)) {
+
+ return FALSE;
+
+ }
+
+ for (i=0; i < a1->n_alert_commands; i++) {
+ if (a1->alert_commands[i] != a2->alert_commands[i])
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* the return value is the number of alerts added to alist */
+static khm_int32
+alert_consolidate(alert_list * alist,
+ khui_alert * alert,
+ khm_boolean add_from_queue) {
+
+ khui_alert * listtop;
+ int queue_size = 0;
+ int i;
+ khm_int32 n_added = 0;
+
+#ifdef DEBUG
+ assert(alist);
+#endif
+
+ if (alist->n_alerts == ARRAYLENGTH(alist->alerts)) {
+ /* can't add anything */
+
+ return 0;
+ }
+
+ /* if the list is empty, we just add one alert */
+ if (alist->n_alerts == 0) {
+
+ if (alert) {
+ khui_alert_lock(alert);
+ if (alert_can_consolidate(NULL, alert, alist)) {
+ alert_list_add_alert(alist, alert);
+ n_added ++;
+ alert = NULL;
+ }
+ khui_alert_unlock(alert);
+ }
+
+ if (n_added == 0 && add_from_queue) {
+ khui_alert * q;
+ int i;
+
+ queue_size = alert_queue_get_size();
+ for (i=0; i < queue_size && n_added == 0; i++) {
+ q = alert_queue_get_alert_by_pos(i);
+ if (q) {
+ khui_alert_lock(q);
+ if (alert_can_consolidate(NULL, q, alist)) {
+ alert_list_add_alert(alist, q);
+ n_added++;
+ alert_queue_delete_alert(q);
+ }
+ khui_alert_unlock(q);
+ khui_alert_release(q);
+ }
+ }
+ }
+
+ if (n_added == 0) {
+ /* nothing to add */
+ return 0;
+ }
+ }
+
+ /* at this point, the alert list is not empty */
+#ifdef DEBUG
+ assert(alist->n_alerts != 0);
+ assert(alist->alerts[0]);
+#endif
+
+ listtop = alist->alerts[0];
+ khui_alert_hold(listtop);
+ khui_alert_lock(listtop);
+
+ queue_size = alert_queue_get_size();
+
+ if (alert) {
+ khui_alert_lock(alert);
+ if (alert_can_consolidate(listtop, alert, alist)) {
+ alert_list_add_alert(alist, alert);
+ n_added ++;
+ }
+ khui_alert_unlock(alert);
+ }
+
+ if (add_from_queue) {
+ for (i=0; i < queue_size; i++) {
+ khui_alert * a;
+
+ a = alert_queue_get_alert_by_pos(i);
+ if (a == NULL)
+ continue;
+
+ khui_alert_lock(a);
+ if (alert_can_consolidate(listtop, a, alist)) {
+ alert_queue_delete_alert(a);
+ alert_list_add_alert(alist, a);
+ n_added ++;
+
+ queue_size--;
+ i--;
+#ifdef DEBUG
+ assert(alert_queue_get_size() == queue_size);
+#endif
+ }
+ khui_alert_unlock(a);
+ khui_alert_release(a);
+ }
+ }
+
+ khui_alert_unlock(listtop);
+ khui_alert_release(listtop);
+
+ return n_added;
+}
+
+static khm_int32
+alert_check_consolidate_window(alerter_wnd_data * d, khui_alert * a) {
+ alert_list alist;
+ alerter_alert_data * adata;
+ int n_added;
+
+ alert_list_init(&alist);
+
+ adata = QTOP(d);
+ while(adata) {
+
+#ifdef DEBUG
+ assert(adata->alert);
+#endif
+ alert_list_add_alert(&alist, adata->alert);
+
+ adata = QNEXT(adata);
+ }
+
+ n_added = alert_consolidate(&alist, a, FALSE);
+
+ alert_list_destroy(&alist);
+
+ return n_added;
+}
static khm_int32
alert_show_minimized(khui_alert * a) {
- wchar_t tbuf[64];
- wchar_t mbuf[256];
+ wchar_t tbuf[64]; /* corresponds to NOTIFYICONDATA::szInfoTitle[] */
+ wchar_t mbuf[256]; /* corresponds to NOTIFYICONDATA::szInfo[] */
+
+#ifdef DEBUG
+ assert(a);
+#endif
+ if (a == NULL)
+ return KHM_ERROR_INVALID_PARAM;
+
+ khui_alert_lock(a);
if (a->message == NULL)
- return KHM_ERROR_SUCCESS;
+ goto done;
if (a->title == NULL) {
LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
@@ -454,24 +2029,41 @@ alert_show_minimized(khui_alert * a) { a->flags |= KHUI_ALERT_FLAG_DISPLAY_BALLOON;
-#if (_WIN32_IE >= 0x0501)
- current_alert = a;
- khui_alert_hold(a);
+#ifdef DEBUG
+ assert(balloon_alert == NULL);
#endif
+ if (balloon_alert) {
+ khui_alert_lock(balloon_alert);
+ balloon_alert->displayed = FALSE;
+ khui_alert_unlock(balloon_alert);
+ khui_alert_release(balloon_alert);
+ balloon_alert = NULL;
+ }
+
+ balloon_alert = a;
+ khui_alert_hold(a);
+
+ a->displayed = TRUE;
+
khm_notify_icon_balloon(a->severity,
- tbuf,
- mbuf,
- NTF_TIMEOUT);
+ tbuf,
+ mbuf,
+ NTF_TIMEOUT);
+
+ done:
+ khui_alert_unlock(a);
return KHM_ERROR_SUCCESS;
}
static khm_int32
alert_show_normal(khui_alert * a) {
- HWND hwa;
wchar_t buf[256];
wchar_t * title;
+ alert_list alist;
+
+ khui_alert_lock(a);
if(a->title == NULL) {
LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
@@ -480,73 +2072,226 @@ alert_show_normal(khui_alert * a) { } else
title = a->title;
- /* if we don't have any commands, we just add a "close" button */
- if (a->n_alert_commands == 0) {
- khui_alert_add_command(a, KHUI_PACTION_CLOSE);
- }
+ khui_alert_unlock(a);
- /* if there are other alerts queued up, we should add a 'Next
- alert...' button that when clicked, would show the next queued
- alert. However, we only do this if the current alert doesn't
- actually require a command response. Otherwise, clicking the
- 'next' button will be the equivalent of cancelling out of the
- alert without selecting any of the commands. */
- if (!is_alert_queue_empty() &&
- a->n_alert_commands == 1 &&
- a->alert_commands[0] == KHUI_PACTION_CLOSE) {
+ alert_list_init(&alist);
+ alert_list_set_title(&alist, title);
+ alert_list_add_alert(&alist, a);
- khui_alert_add_command(a, KHUI_PACTION_NEXT);
- }
+ alert_show_list(&alist);
+
+ alert_list_destroy(&alist);
+
+ return KHM_ERROR_SUCCESS;
+}
+
+static khm_int32
+alert_show_list(alert_list * alist) {
+ HWND hwa;
/* we don't need to keep track of the window handle
because the window procedure adds it to the dialog
list automatically */
hwa =
- CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP,
+ CreateWindowEx(ALERT_WINDOW_EX_SYLES,
MAKEINTATOM(atom_alerter),
- title,
- WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN,
+ alist->title,
+ ALERT_WINDOW_STYLES,
0, 0, 300, 300, // bogus values
khm_hwnd_main,
(HMENU) NULL,
khm_hInstance,
- (LPVOID) a);
+ (LPVOID) alist);
ShowWindow(hwa, SW_SHOW);
- return KHM_ERROR_SUCCESS;
+ return (hwa != NULL);
}
static khm_int32
alert_show(khui_alert * a) {
+ khm_boolean show_normal = FALSE;
+ khm_boolean show_mini = FALSE;
+
+ khui_alert_lock(a);
+
/* is there an alert already? If so, we just enqueue the message
and let it sit. */
- if (current_alert) {
- return alert_enqueue(a);
+ if (ALERT_DISPLAYED() &&
+ !(a->flags & KHUI_ALERT_FLAG_MODAL)) {
+ khm_int32 rv;
+ alerter_wnd_data * wdata;
+
+ khui_alert_unlock(a);
+
+ /* if there are any alerter windows displayed, check if this
+ alert can be consolidated with any of them. If so, we
+ should consolidate it. Otherwise, just enqueue it. */
+ for(wdata = khui_alert_windows;
+ wdata;
+ wdata = LNEXT(wdata)) {
+ if (alert_check_consolidate_window(wdata, a)) {
+
+ add_alert_to_wnd_data(wdata, a);
+ estimate_alerter_wnd_sizes(wdata);
+ setup_alerter_window_controls(wdata);
+
+ return KHM_ERROR_SUCCESS;
+
+ }
+ }
+
+ rv = alert_enqueue(a);
+
+ if (KHM_SUCCEEDED(rv))
+ return KHM_ERROR_HELD;
+ else
+ return rv;
}
- /* the window has already been shown */
if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) ||
- ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&
- !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW)))
- return KHM_ERROR_SUCCESS;
+ ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&
+ !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW))) {
- if(a->err_context != NULL ||
- a->err_event != NULL) {
- khui_alert_lock(a);
- a->flags |= KHUI_ALERT_FLAG_VALID_ERROR;
- khui_alert_unlock(a);
+ /* The alert has already been displayed. */
+
+ show_normal = FALSE;
+ show_mini = FALSE;
+
+ } else {
+
+ if(a->err_context != NULL ||
+ a->err_event != NULL) {
+ a->flags |= KHUI_ALERT_FLAG_VALID_ERROR;
+ }
+
+ /* depending on the state of the main window, we
+ need to either show a window or a balloon */
+ if ((a->flags & KHUI_ALERT_FLAG_MODAL) ||
+ (khm_is_main_window_active() &&
+ !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)) ||
+ (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW)) {
+
+ show_normal = TRUE;
+
+ } else {
+
+ show_mini = TRUE;
+
+ }
}
- /* depending on the state of the main window, we
- need to either show a window or a balloon */
- if ((a->flags & KHUI_ALERT_FLAG_MODAL) ||
- (khm_is_main_window_active() &&
- !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)))
+ khui_alert_unlock(a);
+
+ if (show_normal)
return alert_show_normal(a);
- else
+ else if (show_mini)
return alert_show_minimized(a);
+ else
+ return KHM_ERROR_SUCCESS;
+}
+
+static void
+show_queued_alerts(void) {
+
+ if (!ALERT_DISPLAYED()) {
+
+ /* show next consolidated batch */
+ alert_list alist;
+ int n;
+
+ alert_list_init(&alist);
+ n = alert_consolidate(&alist, NULL, TRUE);
+
+ if (n) {
+ if (n == 1) {
+ khui_alert_lock(alist.alerts[0]);
+
+ if (alist.alerts[0]->title) {
+ alert_list_set_title(&alist, alist.alerts[0]->title);
+ } else {
+ wchar_t title[KHUI_MAXCCH_TITLE];
+ LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+ title, ARRAYLENGTH(title));
+ alert_list_set_title(&alist, title);
+ }
+
+ khui_alert_unlock(alist.alerts[0]);
+ } else {
+ wchar_t title[KHUI_MAXCCH_TITLE];
+ LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+ title, ARRAYLENGTH(title));
+ alert_list_set_title(&alist, title);
+ }
+
+ alert_show_list(&alist);
+ }
+
+ alert_list_destroy(&alist);
+
+ if (n == 0) {
+ khui_alert * a;
+
+ /* no alerts were shown above. This maybe because none of
+ the alerts were consolidatable or they were requested
+ to be shown in a balloon. In this case, we just take
+ the first alert from the queue and show it manually. */
+
+ a = alert_queue_get_alert();
+ if (a) {
+ alert_show(a);
+ khui_alert_release(a);
+ }
+ }
+
+ check_for_queued_alerts();
+ }
+}
+
+
+static void
+check_for_queued_alerts(void) {
+ if (!is_alert_queue_empty()) {
+ khui_alert * a;
+
+ a = alert_queue_peek();
+
+ khui_alert_lock(a);
+
+ if (a->title) {
+ HICON hi;
+ int res;
+
+ if (a->severity == KHERR_ERROR)
+ res = OIC_ERROR;
+ else if (a->severity == KHERR_WARNING)
+ res = OIC_WARNING;
+ else
+ res = OIC_INFORMATION;
+
+ hi = LoadImage(0, MAKEINTRESOURCE(res),
+ IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
+ LR_SHARED);
+
+ khm_statusbar_set_part(KHUI_SBPART_NOTICE,
+ hi,
+ a->title);
+ } else {
+ khm_statusbar_set_part(KHUI_SBPART_NOTICE,
+ NULL, NULL);
+#ifdef DEBUG
+ DebugBreak();
+#endif
+ }
+
+ khui_alert_unlock(a);
+ khui_alert_release(a);
+
+ } else {
+ khm_statusbar_set_part(KHUI_SBPART_NOTICE,
+ NULL, NULL);
+ }
}
static khm_int32
@@ -554,7 +2299,7 @@ alert_enqueue(khui_alert * a) { if (is_alert_queue_full())
return KHM_ERROR_NO_RESOURCES;
- add_to_alert_queue(a);
+ alert_queue_put_alert(a);
check_for_queued_alerts();
return KHM_ERROR_SUCCESS;
@@ -570,29 +2315,14 @@ alerter_wnd_proc(HWND hwnd, switch(uMsg) {
case WM_CREATE:
{
- LONG dlgb;
- HWND hwnd_parent;
- RECT r_parent;
- POINT pos;
- SIZE s;
LPCREATESTRUCT lpcs;
- khui_alert * a;
+ alert_list * alist;
alerter_wnd_data * d;
lpcs = (LPCREATESTRUCT) lParam;
- a = (khui_alert *) lpcs->lpCreateParams;
- khui_alert_hold(a);
-
- d = PMALLOC(sizeof(*d));
- ZeroMemory(d, sizeof(*d));
-
- d->alert = a;
- d->hwnd = hwnd;
-
- khui_alert_lock(a);
+ alist = (alert_list *) lpcs->lpCreateParams;
- a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW;
- LPUSH(&khui_alerts, d);
+ d = create_alerter_wnd_data(hwnd, alist);
#pragma warning(push)
#pragma warning(disable: 4244)
@@ -602,444 +2332,424 @@ alerter_wnd_proc(HWND hwnd, khm_add_dialog(hwnd);
khm_enter_modal(hwnd);
- /* now figure out the size and position of the window */
+ estimate_alerter_wnd_sizes(d);
+ setup_alerter_window_controls(d);
- hwnd_parent = GetWindow(hwnd, GW_OWNER);
- GetWindowRect(hwnd_parent, &r_parent);
-
- dlgb = GetDialogBaseUnits();
+ if (d->hw_close) {
+ SetFocus(d->hw_close);
+ }
-#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4)
-#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8)
+ return TRUE;
+ }
+ break; /* not reached */
- d->dx_margin = DLG2SCNX(NTF_MARGIN);
- d->dy_margin = DLG2SCNY(NTF_MARGIN);
+ case WM_DESTROY:
+ {
+ alerter_wnd_data * d;
- d->x_message = DLG2SCNX(NTF_MSG_X);
- d->dx_message = DLG2SCNX(NTF_MSG_WIDTH);
+ /* khm_leave_modal() could be here, but instead it is in
+ the WM_COMMAND handler. This is because the modal loop
+ has to be exited before DestroyWindow() is issued. */
+ //khm_leave_modal();
+ khm_del_dialog(hwnd);
- if (a->message) {
- d->dy_message = DLG2SCNY(NTF_MSG_HEIGHT);
- }
+ d = (alerter_wnd_data *)(LONG_PTR)
+ GetWindowLongPtr(hwnd, NTF_PARAM);
- if (a->suggestion) {
- d->dy_suggestion = DLG2SCNY(NTF_SUG_HEIGHT);
- d->dx_suggest_pad = DLG2SCNX(NTF_SUG_PAD);
- }
+ destroy_alerter_wnd_data(d);
- d->dy_bb = DLG2SCNY(NTF_BB_HEIGHT);
- d->dx_button = DLG2SCNX(NTF_BUTTON_WIDTH);
- d->dy_button = DLG2SCNY(NTF_BUTTON_HEIGHT);
- d->dx_button_incr = DLG2SCNX(NTF_BUTTON_XINCR);
+ return TRUE;
+ }
+ break;
- d->dx_icon = DLG2SCNX(NTF_ICON_WIDTH);
- d->dy_icon = DLG2SCNY(NTF_ICON_HEIGHT);
+ case WM_COMMAND:
+ {
+ alerter_wnd_data * d;
- d->dx_client = DLG2SCNX(NTF_WIDTH);
- d->dy_client = max(d->dy_icon,
- d->dy_message +
- ((d->dy_suggestion > 0)?
- (d->dy_suggestion + d->dy_margin):
- 0)) +
- d->dy_margin * 3 + d->dy_bb;
+ d = (alerter_wnd_data *)(LONG_PTR)
+ GetWindowLongPtr(hwnd, NTF_PARAM);
- /* adjust for client rect */
- s.cx = d->dx_client;
- s.cy = d->dy_client;
+ if(HIWORD(wParam) == BN_CLICKED) {
+ if (LOWORD(wParam) == IDC_NTF_CLOSE ||
+ LOWORD(wParam) == KHUI_PACTION_NEXT) {
- {
- RECT c_r;
- RECT w_r;
+ khm_leave_modal();
- GetWindowRect(hwnd, &w_r);
- GetClientRect(hwnd, &c_r);
+ DestroyWindow(hwnd);
- s.cx += (w_r.right - w_r.left) - (c_r.right - c_r.left);
- s.cy += (w_r.bottom - w_r.top) - (c_r.bottom - c_r.top);
+ return 0;
+ }
}
+ }
+ break;
- pos.x = (r_parent.left + r_parent.right - s.cx) / 2;
- pos.y = (r_parent.top + r_parent.bottom - s.cy) / 2;
+ case WM_CLOSE:
+ {
+ khm_leave_modal();
- SetWindowPos(hwnd,
- HWND_TOP,
- pos.x, pos.y,
- s.cx, s.cy,
- SWP_SHOWWINDOW);
+ DestroyWindow(hwnd);
- {
- LOGFONT lf;
- HDC hdc_dt;
-
- hdc_dt = GetDC(NULL);
-
- lf.lfHeight = -MulDiv(8,
- GetDeviceCaps(hdc_dt, LOGPIXELSY),
- 72);
- lf.lfWidth = 0;
- lf.lfEscapement = 0;
- lf.lfOrientation = 0;
- lf.lfWeight = FW_NORMAL;
- lf.lfItalic = FALSE;
- lf.lfUnderline = FALSE;
- lf.lfStrikeOut = FALSE;
- lf.lfCharSet = DEFAULT_CHARSET;
- lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
- lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
- lf.lfQuality = DEFAULT_QUALITY;
- lf.lfPitchAndFamily = DEFAULT_PITCH;
-
- LoadString(khm_hInstance, IDS_DEFAULT_FONT,
- lf.lfFaceName, ARRAYLENGTH(lf.lfFaceName));
-
- d->hfont = CreateFontIndirect(&lf);
-
- ReleaseDC(NULL, hdc_dt);
- }
+ return 0;
+ }
+ }
- /* create dialog controls now */
- {
- int x,y;
- int width, height;
- int i;
+ /* Since this is a custom built dialog, we use DefDlgProc instead
+ of DefWindowProc. */
+ return DefDlgProc(hwnd, uMsg, wParam, lParam);
+}
- x = d->x_message;
- y = d->dy_client - d->dy_bb;
- width = d->dx_button;
- height = d->dy_button;
+static LRESULT CALLBACK
+alert_bin_wnd_proc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ BOOL in_printclient = FALSE;
- for(i=0; i<a->n_alert_commands; i++) {
- wchar_t caption[256];
- khui_action * action;
- HWND hw_button;
+ switch(uMsg) {
+ case WM_CREATE:
+ {
+ LPCREATESTRUCT lpcs;
+ alerter_wnd_data * d;
- if(a->alert_commands[i] == 0)
- continue;
+ lpcs = (LPCREATESTRUCT) lParam;
+ d = (alerter_wnd_data *) lpcs->lpCreateParams;
- action = khui_find_action(a->alert_commands[i]);
- if(action == NULL)
- continue;
+#pragma warning(push)
+#pragma warning(disable: 4244)
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) d);
+#pragma warning(pop)
+ }
+ return 0;
- LoadString(khm_hInstance, action->is_caption,
- caption, ARRAYLENGTH(caption));
-
- hw_button =
- CreateWindowEx(0,
- L"BUTTON",
- caption,
- WS_VISIBLE | WS_CHILD |
- /* the first button is the default */
- ((i==0)? BS_DEFPUSHBUTTON: 0),
- x,y,width,height,
- hwnd,
- (HMENU)(INT_PTR) (action->cmd),
- khm_hInstance,
- NULL);
+ case WM_ERASEBKGND:
+ /* we erase the background when we are drawing the alerts
+ anyway. */
+ return 0;
- SendMessage(hw_button, WM_SETFONT,
- (WPARAM) d->hfont, MAKELPARAM(TRUE, 0));
+ case WM_PRINTCLIENT:
+ in_printclient = TRUE;
+ /* fallthrough */
+ case WM_PAINT:
+ {
+ HDC hdc;
+ PAINTSTRUCT ps;
+ RECT r;
+ HFONT hf_old;
+ int y;
+ alerter_wnd_data * d;
+ alerter_alert_data * adata;
+ size_t len;
+ int idx;
- d->hwnd_buttons[i] = hw_button;
+ d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+ assert(d);
+#endif
- x += d->dx_button_incr;
- }
+ if (in_printclient) {
+ hdc = (HDC) wParam;
+ } else {
+ hdc = BeginPaint(hwnd, &ps);
}
- if (d->hwnd_buttons[0])
- SetFocus(d->hwnd_buttons[0]);
-
- khm_notify_icon_change(a->severity);
-
- khui_alert_unlock(a);
-
- d->metrics_done = FALSE;
-
- return TRUE;
- }
- break; /* not reached */
+#ifdef DEBUG
+ assert(hdc);
+ assert(d->hfont);
+#endif
- case WM_DESTROY:
- {
- alerter_wnd_data * d;
+#ifdef ALERT_STATIC_BACKGROUND
+ if (in_printclient || ps.fErase) {
+ HBRUSH hb_background;
- /* khm_leave_modal() could be here, but instead it is in
- the WM_COMMAND handler. This is because the modal loop
- has to be exited before DestroyWindow() is issued. */
- //khm_leave_modal();
- khm_del_dialog(hwnd);
+ hb_background = GetSysColorBrush(COLOR_BTNFACE);
- d = (alerter_wnd_data *)(LONG_PTR)
- GetWindowLongPtr(hwnd, NTF_PARAM);
+ GetClientRect(hwnd, &r);
+ FillRect(hdc, &r, hb_background);
+ }
+#endif
- LDELETE(&khui_alerts, d);
+ SetBkMode(hdc, TRANSPARENT);
- khui_alert_lock(d->alert);
- d->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW;
- if (d->alert->flags & KHUI_ALERT_FLAG_MODAL)
- notifier_modal_loop = FALSE;
- khui_alert_unlock(d->alert);
+ hf_old = SelectFont(hdc, d->hfont);
- khui_alert_release(d->alert);
+ y = -d->scroll_top;
+ idx = 0;
+ /* go through the alerts and display them */
+ adata = QTOP(d);
+ while(adata) {
+ khui_alert * a;
+
+#ifndef ALERT_STATIC_BACKGROUND
+#define MIX_C(v1, v2, p) (((int)v1) * p + (((int) v2) * (256 - p)))
+#define ALPHA 50
+ if (in_printclient || ps.fErase) {
+ TRIVERTEX v[2];
+ GRADIENT_RECT gr;
+ COLORREF clr;
+ COLORREF clr2;
+
+ CopyRect(&r, &adata->r_alert);
+ OffsetRect(&r, 0, y);
+
+ v[0].x = r.left;
+ v[0].y = r.top;
+ v[0].Alpha = 0;
+
+ v[1].x = r.right;
+ v[1].y = r.bottom;
+ v[1].Alpha = 0;
+
+ if (idx == d->c_alert) {
+ clr = GetSysColor(COLOR_HOTLIGHT);
+
+ clr2 = GetSysColor(COLOR_BTNHIGHLIGHT);
+ v[0].Red = MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA);
+ v[0].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA);
+ v[0].Blue = MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA);
+
+ clr2 = GetSysColor(COLOR_BTNFACE);
+ v[1].Red = MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA);
+ v[1].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA);
+ v[1].Blue = MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA);
+ } else {
+ clr = GetSysColor(COLOR_BTNHIGHLIGHT);
+ v[0].Red = ((int)GetRValue(clr)) << 8;
+ v[0].Green = ((int)GetGValue(clr)) << 8;
+ v[0].Blue = ((int)GetBValue(clr)) << 8;
+
+ clr = GetSysColor(COLOR_BTNFACE);
+ v[1].Red = ((int)GetRValue(clr)) << 8;
+ v[1].Green = ((int)GetGValue(clr)) << 8;
+ v[1].Blue = ((int)GetBValue(clr)) << 8;
+ }
- DeleteObject(d->hfont);
+ gr.UpperLeft = 0;
+ gr.LowerRight = 1;
+ GradientFill(hdc, v, 2, &gr, 1, GRADIENT_FILL_RECT_V);
+ }
+#undef ALPHA
+#undef MIX_C
+#endif
- PFREE(d);
+ a = adata->alert;
+#ifdef DEBUG
+ assert(a != NULL);
+#endif
+ khui_alert_lock(a);
- khm_notify_icon_change(KHERR_NONE);
+ if (!IsRectEmpty(&adata->r_title)) {
- return TRUE;
- }
- break;
+ CopyRect(&r, &adata->r_title);
+ OffsetRect(&r, 0, y);
- case WM_PAINT:
- {
- RECT r_update;
- PAINTSTRUCT ps;
- HDC hdc;
- LONG dlgb;
- alerter_wnd_data * d;
- HFONT hf_old;
- BOOL need_resize = FALSE;
+ StringCchLength(a->title, KHUI_MAXCCH_TITLE, &len);
- if(!GetUpdateRect(hwnd, &r_update, TRUE))
- return FALSE;
+ DrawEdge(hdc, &r, EDGE_RAISED, BF_RECT | BF_MIDDLE);
- d = (alerter_wnd_data *)(LONG_PTR)
- GetWindowLongPtr(hwnd, NTF_PARAM);
+ InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);
- dlgb = GetDialogBaseUnits();
+ DrawText(hdc, a->title, (int) len, &r,
+ DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+ }
- hdc = BeginPaint(hwnd, &ps);
+ {
+ HICON hicon;
+ int iid;
+
+ CopyRect(&r, &adata->r_icon);
+ OffsetRect(&r, 0, y);
+
+ if(a->severity == KHERR_ERROR)
+ iid = OIC_HAND;
+ else if(a->severity == KHERR_WARNING)
+ iid = OIC_BANG;
+ else
+ iid = OIC_NOTE;
+
+ hicon = (HICON) LoadImage(NULL,
+ MAKEINTRESOURCE(iid),
+ IMAGE_ICON,
+ GetSystemMetrics(SM_CXICON),
+ GetSystemMetrics(SM_CYICON),
+ LR_SHARED);
+
+ DrawIcon(hdc, r.left, r.top, hicon);
+ }
- hf_old = SelectFont(hdc, d->hfont);
+ if (a->message) {
- khui_alert_lock(d->alert);
+ CopyRect(&r, &adata->r_message);
+ OffsetRect(&r, 0, y);
- // draw the severity icon
- {
- HICON hicon;
- int x,y;
- int iid;
-
- /* GOINGHERE! If the metrics for the window haven't
- been calculated yet, then calculate them. If the
- hight needs to be expanded, then do that and wait
- for the next repaint cycle. Also move the button
- controls down. */
- x = d->dx_margin;
- y = d->dy_margin;
-
- if(d->alert->severity == KHERR_ERROR)
- iid = OIC_HAND;
- else if(d->alert->severity == KHERR_WARNING)
- iid = OIC_BANG;
- else
- iid = OIC_NOTE;
-
- hicon = LoadImage(NULL,
- MAKEINTRESOURCE(iid),
- IMAGE_ICON,
- GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
- LR_SHARED);
-
- DrawIcon(hdc, x, y, hicon);
- }
+ StringCchLength(a->message, KHUI_MAXCCH_MESSAGE, &len);
- // draw the message
- if(d->alert->message) {
- RECT r;
- int width;
- int height;
- size_t cch;
-
- r.left = d->x_message;
- r.top = d->dy_margin;
- width = d->dx_message;
- r.right = r.left + width;
- height = d->dy_message;
- r.bottom = r.top + height;
-
- StringCchLength(d->alert->message,
- KHUI_MAXCCH_MESSAGE, &cch);
-
- height = DrawText(hdc,
- d->alert->message,
- (int) cch,
- &r,
- DT_WORDBREAK |
- DT_CALCRECT);
-
- if (height > d->dy_message) {
- d->dy_message = height;
- need_resize = TRUE;
- } else {
- DrawText(hdc,
- d->alert->message,
- (int) cch,
- &r,
+ DrawText(hdc, a->message, (int) len, &r,
DT_WORDBREAK);
}
- d->y_message = r.top;
- }
+ if (a->suggestion) {
+ HICON hicon;
+ SIZE sz;
- // and the suggestion
- if (d->alert->suggestion) {
- RECT r, ro;
- int height;
- size_t cch;
- HICON h_sug_ico;
-
- r.left = d->x_message;
- r.top = d->y_message + d->dy_message + d->dy_margin;
- r.right = r.left + d->dx_message;
- r.bottom = r.top + d->dy_suggestion;
-
- CopyRect(&ro, &r);
-
- // adjust for icon and padding
- r.left += GetSystemMetrics(SM_CXSMICON) + d->dx_suggest_pad * 2;
- r.top += d->dx_suggest_pad;
- r.right -= d->dx_suggest_pad;
- r.bottom -= d->dx_suggest_pad;
-
- StringCchLength(d->alert->suggestion,
- KHUI_MAXCCH_SUGGESTION, &cch);
-
- height = DrawText(hdc,
- d->alert->suggestion,
- (int) cch,
- &r,
- DT_WORDBREAK |
- DT_CALCRECT);
-
- if (height > d->dy_suggestion) {
- d->dy_suggestion = height;
- need_resize = TRUE;
- } else {
- int old_bk_mode;
+ CopyRect(&r, &adata->r_suggestion);
+ OffsetRect(&r, 0, y);
- ro.bottom = r.bottom + d->dx_suggest_pad;
+ DrawEdge(hdc, &r, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
- FillRect(hdc, &ro, (HBRUSH) (COLOR_INFOBK + 1));
- DrawEdge(hdc, &ro, EDGE_SUNKEN, BF_FLAT | BF_RECT);
+ InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);
- h_sug_ico =
- LoadImage(0,
- MAKEINTRESOURCE(OIC_INFORMATION),
- IMAGE_ICON,
- GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
- LR_SHARED);
+ sz.cx = GetSystemMetrics(SM_CXSMICON);
+ sz.cy = GetSystemMetrics(SM_CYSMICON);
- assert(h_sug_ico != NULL);
+ hicon = (HICON) LoadImage(NULL,
+ MAKEINTRESOURCE(OIC_NOTE),
+ IMAGE_ICON,
+ sz.cx,
+ sz.cy,
+ LR_SHARED);
- DrawIconEx(hdc,
- ro.left + d->dx_suggest_pad,
- ro.top + d->dx_suggest_pad,
- h_sug_ico,
- GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
- 0, NULL,
+ DrawIconEx(hdc, r.left, r.top, hicon, sz.cx, sz.cy, 0, NULL,
DI_NORMAL);
- old_bk_mode = SetBkMode(hdc, TRANSPARENT);
+ r.left += d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);
- DrawText(hdc,
- d->alert->suggestion,
- (int) cch,
- &r,
- DT_WORDBREAK);
+ StringCchLength(a->suggestion, KHUI_MAXCCH_SUGGESTION, &len);
- SetBkMode(hdc, old_bk_mode);
+ DrawText(hdc, a->suggestion, (int) len, &r,
+ DT_WORDBREAK);
}
+ khui_alert_unlock(a);
+
+ y += adata->r_alert.bottom;
+ idx++;
- d->y_suggestion = r.top;
+ adata = QNEXT(adata);
}
- khui_alert_unlock(d->alert);
+ SelectFont(hdc, hf_old);
- SelectObject(hdc, hf_old);
+ if (!in_printclient) {
+ EndPaint(hwnd, &ps);
+ }
+ }
+ return 0;
- EndPaint(hwnd, &ps);
+ case WM_VSCROLL:
+ {
+ alerter_wnd_data * d;
+ int new_pos = 0;
+ SCROLLINFO si;
- if (need_resize) {
- RECT r;
- int x,y;
- int width, height;
- int i;
-
- GetClientRect(hwnd, &r);
+ d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+ assert(d);
+#endif
+ if (d == NULL)
+ break; /* we can't handle the message */
- height = max(d->dy_icon,
- d->dy_message +
- ((d->dy_suggestion > 0)?
- (d->dy_suggestion + d->dy_margin):
- 0)) +
- d->dy_margin * 3 + d->dy_bb;
- r.bottom = r.top + height;
-
- d->dy_client = height;
-
- AdjustWindowRectEx(&r,
- GetWindowLongPtr(hwnd, GWL_STYLE),
- FALSE,
- GetWindowLongPtr(hwnd, GWL_EXSTYLE));
-
- SetWindowPos(hwnd,
- NULL,
- 0, 0,
- r.right - r.left,
- r.bottom - r.top,
- SWP_NOACTIVATE | SWP_NOCOPYBITS |
- SWP_NOMOVE | SWP_NOOWNERZORDER |
- SWP_NOZORDER);
-
- InvalidateRect(hwnd, NULL, TRUE);
-
- x = d->x_message;
- y = d->dy_client - d->dy_bb;
- width = d->dx_button;
- height = d->dy_button;
-
- for(i=0; i<d->alert->n_alert_commands; i++) {
- MoveWindow(d->hwnd_buttons[i],
- x,y,
- width,height,
- TRUE);
-
- x += d->dx_button_incr;
- }
+ ZeroMemory(&si, sizeof(si));
+
+ switch(LOWORD(wParam)) {
+ case SB_BOTTOM:
+ new_pos = d->s_alerts.cy - d->cy_max_wnd;
+ break;
+
+ case SB_LINEDOWN:
+ new_pos = d->scroll_top + SCROLL_LINE_SIZE(d);
+ break;
+
+ case SB_LINEUP:
+ new_pos = d->scroll_top - SCROLL_LINE_SIZE(d);
+ break;
+
+ case SB_PAGEDOWN:
+ new_pos = d->scroll_top + d->cy_max_wnd;
+ break;
+
+ case SB_PAGEUP:
+ new_pos = d->scroll_top - d->cy_max_wnd;
+ break;
+
+ case SB_THUMBPOSITION:
+ case SB_THUMBTRACK:
+ si.fMask = SIF_TRACKPOS;
+ GetScrollInfo(hwnd, SB_VERT, &si);
+ new_pos = si.nTrackPos;
+ break;
+
+ case SB_TOP:
+ new_pos = 0;
+ break;
+
+ case SB_ENDSCROLL:
+ si.fMask = SIF_POS;
+ si.nPos = d->scroll_top;
+ SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+ return 0;
+
+ default:
+ return 0;
}
- return FALSE;
+ scroll_to_position(d, new_pos, FALSE);
}
- break; /* not reached */
+ return 0;
case WM_COMMAND:
{
alerter_wnd_data * d;
- d = (alerter_wnd_data *)(LONG_PTR)
- GetWindowLongPtr(hwnd, NTF_PARAM);
+ d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+ assert(d);
+#endif
+ if (d == NULL)
+ break;
- if(HIWORD(wParam) == BN_CLICKED) {
- khui_alert_lock(d->alert);
- d->alert->response = LOWORD(wParam);
- khui_alert_unlock(d->alert);
+ if (HIWORD(wParam) == BN_CLICKED) {
+ process_command_button(d, LOWORD(wParam));
+ return 0;
+ } else if (HIWORD(wParam) == BN_SETFOCUS) {
+ ensure_command_is_visible(d, LOWORD(wParam));
+ return 0;
+ }
+ }
+ break;
- khm_leave_modal();
+ case WM_LBUTTONUP:
+ {
+ alerter_wnd_data * d;
+ int x,y;
- DestroyWindow(hwnd);
+ d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+ assert(d);
+#endif
+ if (d == NULL)
+ break;
- if (LOWORD(wParam) == KHUI_PACTION_NEXT)
- kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0);
- return 0;
- }
+ x = GET_X_LPARAM(lParam);
+ y = GET_Y_LPARAM(lParam);
+
+ handle_mouse_select(d, x, y);
}
break;
+
+ case WM_SIZE:
+ {
+ InvalidateRect(hwnd, NULL, TRUE);
+ }
+ break;
+
+ case WM_DESTROY:
+ {
+ /* nothing needs to be done here */
+ }
+ return 0;
}
- return DefDlgProc(hwnd, uMsg, wParam, lParam);
- //return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
ATOM khm_register_alerter_wnd_class(void)
@@ -1061,7 +2771,7 @@ ATOM khm_register_alerter_wnd_class(void) wcx.hInstance = khm_hInstance;
wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP));
wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
- wcx.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wcx.lpszMenuName = NULL;
wcx.lpszClassName = KHUI_ALERTER_CLASS;
wcx.hIconSm = NULL;
@@ -1071,6 +2781,31 @@ ATOM khm_register_alerter_wnd_class(void) return atom_alerter;
}
+ATOM khm_register_alert_bin_wnd_class(void)
+{
+ WNDCLASSEX wcx;
+
+ ZeroMemory(&wcx, sizeof(wcx));
+
+ wcx.cbSize = sizeof(wcx);
+ wcx.style = CS_OWNDC;
+
+ wcx.lpfnWndProc = alert_bin_wnd_proc;
+ wcx.cbClsExtra = 0;
+ wcx.cbWndExtra = sizeof(LONG_PTR);
+ wcx.hInstance = khm_hInstance;
+ wcx.hIcon = NULL;
+ wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
+ wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+ wcx.lpszMenuName = NULL;
+ wcx.lpszClassName = KHUI_ALERTBIN_CLASS;
+ wcx.hIconSm = NULL;
+
+ atom_alert_bin = RegisterClassEx(&wcx);
+
+ return atom_alert_bin;
+}
+
/**********************************************************************
Notification Icon
***********************************************************************/
@@ -1096,11 +2831,11 @@ void khm_notify_icon_add(void) { Shell_NotifyIcon(NIM_ADD, &ni);
+ DestroyIcon(ni.hIcon);
+
ni.cbSize = sizeof(ni);
ni.uVersion = NOTIFYICON_VERSION;
Shell_NotifyIcon(NIM_SETVERSION, &ni);
-
- DestroyIcon(ni.hIcon);
}
void
@@ -1140,19 +2875,20 @@ khm_notify_icon_balloon(khm_int32 severity, /* too long? */
StringCchCopyN(ni.szInfo, ARRAYLENGTH(ni.szInfo),
msg,
- ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELIPSIS));
+ ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELLIPSIS));
StringCchCat(ni.szInfo, ARRAYLENGTH(ni.szInfo),
- ELIPSIS);
+ ELLIPSIS);
}
if (FAILED(StringCbCopy(ni.szInfoTitle, sizeof(ni.szInfoTitle),
title))) {
StringCchCopyN(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),
title,
- ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELIPSIS));
+ ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELLIPSIS));
StringCchCat(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),
- ELIPSIS);
+ ELLIPSIS);
}
+
ni.uTimeout = timeout;
Shell_NotifyIcon(NIM_MODIFY, &ni);
@@ -1177,7 +2913,7 @@ void khm_notify_icon_expstate(enum khm_notif_expstate expseverity) { iid_normal = new_iid;
- if (current_alert == NULL)
+ if (balloon_alert == NULL)
khm_notify_icon_change(KHERR_NONE);
}
@@ -1227,6 +2963,102 @@ void khm_notify_icon_remove(void) { Shell_NotifyIcon(NIM_DELETE, &ni);
}
+static khm_int32
+get_default_notifier_action(void) {
+ khm_int32 def_cmd = KHUI_ACTION_OPEN_APP;
+ khm_handle csp_cw = NULL;
+ khm_size i;
+
+ if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ,
+ &csp_cw)))
+ def_cmd;
+
+ khc_read_int32(csp_cw, L"NotificationAction", &def_cmd);
+
+ khc_close_space(csp_cw);
+
+ for (i=0; i < n_khm_notifier_actions; i++) {
+ if (khm_notifier_actions[i] == def_cmd)
+ break;
+ }
+
+ if (i < n_khm_notifier_actions)
+ return def_cmd;
+ else
+ return KHUI_ACTION_OPEN_APP;
+}
+
+void khm_notify_icon_activate(void) {
+ /* if there are any notifications waiting to be shown and there
+ are no alerts already being shown, we show them. Otherwise we
+ execute the default action. */
+
+ khm_notify_icon_change(KHERR_NONE);
+
+ if (balloon_alert != NULL && khui_alert_windows == NULL) {
+
+ khui_alert * a;
+ khm_boolean alert_done = FALSE;
+
+ a = balloon_alert;
+ balloon_alert = NULL;
+
+ khui_alert_lock(a);
+
+ a->displayed = FALSE;
+
+ if ((a->flags & KHUI_ALERT_FLAG_DEFACTION) &&
+ (a->n_alert_commands > 0)) {
+
+ PostMessage(khm_hwnd_main, WM_COMMAND,
+ MAKEWPARAM(a->alert_commands[0],
+ 0),
+ 0);
+ alert_done = TRUE;
+
+ } else if (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) {
+
+ alert_show_normal(a);
+ alert_done = TRUE;
+
+ }
+ khui_alert_unlock(a);
+ khui_alert_release(a);
+
+ if (alert_done)
+ return;
+ }
+
+ if (!is_alert_queue_empty() && !ALERT_DISPLAYED()) {
+
+ khm_show_main_window();
+ show_queued_alerts();
+
+ return;
+ }
+
+
+ /* if none of the above applied, then we perform the default
+ action for the notification icon. */
+ {
+ khm_int32 cmd = 0;
+
+ cmd = get_default_notifier_action();
+
+ if (cmd == KHUI_ACTION_OPEN_APP) {
+ if (khm_is_main_window_visible()) {
+ khm_hide_main_window();
+ } else {
+ khm_show_main_window();
+ }
+ } else {
+ khui_action_trigger(cmd, NULL);
+ }
+
+ check_for_queued_alerts();
+ }
+}
+
/*********************************************************************
Initialization
**********************************************************************/
@@ -1239,6 +3071,9 @@ void khm_init_notifier(void) if(!khm_register_alerter_wnd_class())
return;
+ if(!khm_register_alert_bin_wnd_class())
+ return;
+
hwnd_notifier = CreateWindowEx(0,
MAKEINTATOM(atom_notifier),
KHUI_NOTIFIER_WINDOW,
@@ -1255,12 +3090,11 @@ void khm_init_notifier(void) notifier_ready = TRUE;
khm_notify_icon_add();
- }
+ } else {
#ifdef DEBUG
- else {
assert(hwnd_notifier != NULL);
- }
#endif
+ }
khm_timer_init();
khm_addr_change_notifier_init();
@@ -1290,5 +3124,37 @@ void khm_exit_notifier(void) atom_alerter = 0;
}
+ if(atom_alert_bin != 0) {
+ UnregisterClass(MAKEINTATOM(atom_alert_bin), khm_hInstance);
+ atom_alert_bin = 0;
+ }
+
notifier_ready = FALSE;
}
+
+/***** testing *****/
+
+void
+create_test_alerts(void) {
+
+ khui_alert * a;
+ int i;
+
+ for (i=0; i < 50; i++) {
+ wchar_t buf[128];
+
+ StringCbPrintf(buf, sizeof(buf), L"Foo bar baz. This is alert number %d", i);
+ khui_alert_create_simple(L"Title", buf, KHERR_INFO, &a);
+ khui_alert_set_type(a, KHUI_ALERTTYPE_PLUGIN);
+ khui_alert_set_suggestion(a, L"This is a suggestion. It is kinda long to see if the word wrapping actually works as we expect it to. Just in case, here's a line feed.\n\nDoes this show up on a different line? Cool!");
+
+ khui_alert_add_command(a, KHUI_ACTION_NEW_CRED);
+ khui_alert_add_command(a, KHUI_ACTION_CLOSE_APP);
+ khui_alert_add_command(a, KHUI_ACTION_PROPERTIES);
+ khui_alert_add_command(a, KHUI_ACTION_OPEN_APP);
+ khui_alert_add_command(a, KHUI_ACTION_VIEW_REFRESH);
+
+ khui_alert_show(a);
+ khui_alert_release(a);
+ }
+}
|