Changeset 252d03c in mainline


Ignore:
Timestamp:
2021-04-28T09:22:39Z (3 years ago)
Author:
Jiri Svoboda <jiri@…>
Branches:
master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
7470d97
Parents:
de227aba
Message:

Popping up a message, in text mode as well

Location:
uspace
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/uidemo/uidemo.c

    rde227aba r252d03c  
    4646#include <ui/menuentry.h>
    4747#include <ui/menu.h>
     48#include <ui/msgdialog.h>
    4849#include <ui/pbutton.h>
    4950#include <ui/resource.h>
     
    8485};
    8586
     87static void uidemo_file_message(ui_menu_entry_t *, void *);
    8688static void uidemo_file_exit(ui_menu_entry_t *, void *);
     89
     90static void msg_dialog_button(ui_msg_dialog_t *, void *, unsigned);
     91static void msg_dialog_close(ui_msg_dialog_t *, void *);
     92
     93static ui_msg_dialog_cb_t msg_dialog_cb = {
     94        .button = msg_dialog_button,
     95        .close = msg_dialog_close
     96};
    8797
    8898/** Window close button was clicked.
     
    190200}
    191201
    192 /** File/exit menu entry selected.
     202/** File/message menu entry selected.
    193203 *
    194204 * @param mentry Menu entry
    195205 * @param arg Argument (demo)
    196206 */
     207static void uidemo_file_message(ui_menu_entry_t *mentry, void *arg)
     208{
     209        ui_demo_t *demo = (ui_demo_t *) arg;
     210        ui_msg_dialog_params_t mdparams;
     211        ui_msg_dialog_t *dialog;
     212        errno_t rc;
     213
     214        ui_msg_dialog_params_init(&mdparams);
     215        mdparams.caption = "Message For You";
     216        mdparams.text = "Hello, world!";
     217
     218        rc = ui_msg_dialog_create(demo->ui, &mdparams, &dialog);
     219        if (rc != EOK) {
     220                printf("Error creating message dialog.\n");
     221                return;
     222        }
     223
     224        ui_msg_dialog_set_cb(dialog, &msg_dialog_cb, &demo);
     225
     226}
     227
     228/** File/exit menu entry selected.
     229 *
     230 * @param mentry Menu entry
     231 * @param arg Argument (demo)
     232 */
    197233static void uidemo_file_exit(ui_menu_entry_t *mentry, void *arg)
    198234{
     
    200236
    201237        ui_quit(demo->ui);
     238}
     239
     240/** Message dialog button press.
     241 *
     242 * @param dialog Message dialog
     243 * @param arg Argument (ui_demo_t *)
     244 * @param bnum Button number
     245 */
     246static void msg_dialog_button(ui_msg_dialog_t *dialog, void *arg,
     247    unsigned bnum)
     248{
     249        ui_demo_t *demo = (ui_demo_t *) arg;
     250
     251        (void) demo;
     252        ui_msg_dialog_destroy(dialog);
     253}
     254
     255/** Message dialog close request.
     256 *
     257 * @param dialog Message dialog
     258 * @param arg Argument (ui_demo_t *)
     259 */
     260static void msg_dialog_close(ui_msg_dialog_t *dialog, void *arg)
     261{
     262        ui_demo_t *demo = (ui_demo_t *) arg;
     263
     264        (void) demo;
     265        ui_msg_dialog_destroy(dialog);
    202266}
    203267
     
    215279        gfx_bitmap_t *bitmap;
    216280        gfx_coord2_t off;
     281        ui_menu_entry_t *mmsg;
    217282        ui_menu_entry_t *mfoo;
    218283        ui_menu_entry_t *mbar;
     
    228293        }
    229294
     295        memset((void *) &demo, 0, sizeof(demo));
     296        demo.ui = ui;
     297
    230298        ui_wnd_params_init(&params);
    231299        params.caption = "UI Demo";
    232300        params.style |= ui_wds_resizable;
    233         params.rect.p0.x = 0;
    234         params.rect.p0.y = 0;
    235         params.rect.p1.x = 220;
    236         params.rect.p1.y = 350;
    237 
    238         memset((void *) &demo, 0, sizeof(demo));
    239         demo.ui = ui;
     301
     302        /* FIXME: Auto layout */
     303        if (ui_is_textmode(ui)) {
     304                params.rect.p0.x = 0;
     305                params.rect.p0.y = 0;
     306                params.rect.p1.x = 80;
     307                params.rect.p1.y = 25;
     308        } else {
     309                params.rect.p0.x = 0;
     310                params.rect.p0.y = 0;
     311                params.rect.p1.x = 220;
     312                params.rect.p1.y = 350;
     313        }
    240314
    241315        rc = ui_window_create(ui, &params, &window);
     
    269343        }
    270344
     345        rc = ui_menu_entry_create(demo.mfile, "Message", "", &mmsg);
     346        if (rc != EOK) {
     347                printf("Error creating menu.\n");
     348                return rc;
     349        }
     350
     351        ui_menu_entry_set_cb(mmsg, uidemo_file_message, (void *) &demo);
     352
    271353        rc = ui_menu_entry_create(demo.mfile, "Foo", "Ctrl-Alt-Del", &mfoo);
    272354        if (rc != EOK) {
     
    325407        }
    326408
    327         rect.p0.x = 4;
    328         rect.p0.y = 30;
    329         rect.p1.x = 216;
    330         rect.p1.y = 52;
     409        /* FIXME: Auto layout */
     410        if (ui_is_textmode(ui)) {
     411                rect.p0.x = 1;
     412                rect.p0.y = 2;
     413                rect.p1.x = 79;
     414                rect.p1.y = 3;
     415        } else {
     416                rect.p0.x = 4;
     417                rect.p0.y = 30;
     418                rect.p1.x = 216;
     419                rect.p1.y = 52;
     420        }
    331421        ui_menu_bar_set_rect(demo.mbar, &rect);
    332422
  • uspace/lib/ui/include/ui/ui.h

    rde227aba r252d03c  
    4949extern void ui_quit(ui_t *);
    5050extern void ui_run(ui_t *);
     51extern errno_t ui_paint(ui_t *);
    5152extern bool ui_is_textmode(ui_t *);
     53extern bool ui_is_fullscreen(ui_t *);
    5254
    5355#endif
  • uspace/lib/ui/include/ui/wdecor.h

    rde227aba r252d03c  
    4141#include <io/pos_event.h>
    4242#include <stdbool.h>
     43#include <types/ui/event.h>
    4344#include <types/ui/resource.h>
    4445#include <types/ui/wdecor.h>
     
    5152extern void ui_wdecor_set_active(ui_wdecor_t *, bool);
    5253extern errno_t ui_wdecor_paint(ui_wdecor_t *);
    53 extern void ui_wdecor_pos_event(ui_wdecor_t *, pos_event_t *);
     54extern ui_evclaim_t ui_wdecor_pos_event(ui_wdecor_t *, pos_event_t *);
    5455extern void ui_wdecor_rect_from_app(ui_wdecor_style_t, gfx_rect_t *,
    5556    gfx_rect_t *);
  • uspace/lib/ui/include/ui/window.h

    rde227aba r252d03c  
    5353extern void ui_window_add(ui_window_t *, ui_control_t *);
    5454extern void ui_window_remove(ui_window_t *, ui_control_t *);
     55extern ui_window_t *ui_window_get_active(ui_t *);
    5556extern errno_t ui_window_resize(ui_window_t *, gfx_rect_t *);
    5657extern ui_resource_t *ui_window_get_res(ui_window_t *);
  • uspace/lib/ui/private/ui.h

    rde227aba r252d03c  
    3838#define _UI_PRIVATE_UI_H
    3939
     40#include <adt/list.h>
    4041#include <display.h>
    4142#include <io/console.h>
     
    4950        /** Console */
    5051        console_ctrl_t *console;
     52        /** Console GC */
     53        struct console_gc *cgc;
    5154        /** Display */
    5255        display_t *display;
     
    5558        /** @c true if terminating */
    5659        bool quit;
    57         /** Root window (in fullscreen/console mode) */
    58         struct ui_window *root_wnd;
     60        /** Windows (in stacking order, ui_window_t) */
     61        list_t windows;
    5962};
    6063
  • uspace/lib/ui/private/window.h

    rde227aba r252d03c  
    3838#define _UI_PRIVATE_WINDOW_H
    3939
     40#include <adt/list.h>
    4041#include <errno.h>
    4142#include <congfx/console.h>
     
    5556        /** Containing user interface */
    5657        struct ui *ui;
     58        /** Link to @c ui->windows */
     59        link_t lwindows;
    5760        /** Callbacks */
    5861        struct ui_window_cb *cb;
     
    6164        /** Display window */
    6265        display_window_t *dwindow;
    63         /** Console GC */
    64         console_gc_t *cgc;
    6566        /** Window GC */
    6667        gfx_context_t *gc;
  • uspace/lib/ui/src/msgdialog.c

    rde227aba r252d03c  
    4242#include <ui/pbutton.h>
    4343#include <ui/resource.h>
     44#include <ui/ui.h>
    4445#include <ui/window.h>
    4546#include "../private/msgdialog.h"
     
    9798        ui_wnd_params_init(&wparams);
    9899        wparams.caption = params->caption;
    99         wparams.rect.p0.x = 0;
    100         wparams.rect.p0.y = 0;
    101         wparams.rect.p1.x = 200;
    102         wparams.rect.p1.y = 110;
     100
     101        /* FIXME: Auto layout */
     102        if (ui_is_textmode(ui)) {
     103                wparams.rect.p0.x = 0;
     104                wparams.rect.p0.y = 0;
     105                wparams.rect.p1.x = 20;
     106                wparams.rect.p1.y = 7;
     107        } else {
     108                wparams.rect.p0.x = 0;
     109                wparams.rect.p0.y = 0;
     110                wparams.rect.p1.x = 200;
     111                wparams.rect.p1.y = 110;
     112        }
    103113
    104114        rc = ui_window_create(ui, &wparams, &window);
     
    118128                goto error;
    119129
    120         rect.p0.x = 10;
    121         rect.p0.y = 35;
    122         rect.p1.x = 190;
    123         rect.p1.y = 50;
     130        /* FIXME: Auto layout */
     131        if (ui_is_textmode(ui)) {
     132                rect.p0.x = 3;
     133                rect.p0.y = 2;
     134                rect.p1.x = 17;
     135                rect.p1.y = 3;
     136        } else {
     137                rect.p0.x = 10;
     138                rect.p0.y = 35;
     139                rect.p1.x = 190;
     140                rect.p1.y = 50;
     141        }
     142
    124143        ui_label_set_rect(label, &rect);
    125144        ui_label_set_halign(label, gfx_halign_center);
     
    137156        ui_pbutton_set_cb(bok, &ui_msg_dialog_btn_cb, dialog);
    138157
    139         rect.p0.x = 55;
    140         rect.p0.y = 60;
    141         rect.p1.x = 145;
    142         rect.p1.y = 88;
     158        /* FIXME: Auto layout */
     159        if (ui_is_textmode(ui)) {
     160                rect.p0.x = 8;
     161                rect.p0.y = 4;
     162                rect.p1.x = 12;
     163                rect.p1.y = 5;
     164        } else {
     165                rect.p0.x = 55;
     166                rect.p0.y = 60;
     167                rect.p1.x = 145;
     168                rect.p1.y = 88;
     169        }
     170
    143171        ui_pbutton_set_rect(bok, &rect);
    144172
  • uspace/lib/ui/src/ui.c

    rde227aba r252d03c  
    3434 */
    3535
     36#include <adt/list.h>
    3637#include <ctype.h>
    3738#include <display.h>
     
    4546#include <ui/ui.h>
    4647#include <ui/wdecor.h>
     48#include <ui/window.h>
    4749#include "../private/window.h"
    4850#include "../private/ui.h"
     
    104106        display_t *display;
    105107        console_ctrl_t *console;
     108        console_gc_t *cgc;
    106109        ui_winsys_t ws;
    107110        const char *osvc;
     
    131134                        return rc;
    132135                }
     136
     137                rc = console_gc_create(console, NULL, &cgc);
     138                if (rc != EOK) {
     139                        ui_destroy(ui);
     140                        console_done(console);
     141                        return rc;
     142                }
     143
     144                ui->cgc = cgc;
    133145        } else {
    134146                return EINVAL;
     
    154166
    155167        ui->console = console;
     168        list_initialize(&ui->windows);
    156169        *rui = ui;
    157170        return EOK;
     
    173186
    174187        ui->display = disp;
     188        list_initialize(&ui->windows);
    175189        *rui = ui;
    176190        return EOK;
     
    187201
    188202        if (ui->myoutput) {
     203                if (ui->cgc != NULL)
     204                        console_gc_delete(ui->cgc);
    189205                if (ui->console != NULL)
    190206                        console_done(ui->console);
     
    198214static void ui_cons_event_process(ui_t *ui, cons_event_t *event)
    199215{
    200         if (ui->root_wnd == NULL)
     216        ui_window_t *awnd;
     217        ui_evclaim_t claim;
     218
     219        awnd = ui_window_get_active(ui);
     220        if (awnd == NULL)
    201221                return;
    202222
    203223        switch (event->type) {
    204224        case CEV_KEY:
    205                 ui_window_send_kbd(ui->root_wnd, &event->ev.key);
     225                ui_window_send_kbd(awnd, &event->ev.key);
    206226                break;
    207227        case CEV_POS:
    208                 ui_wdecor_pos_event(ui->root_wnd->wdecor, &event->ev.pos);
    209                 ui_window_send_pos(ui->root_wnd, &event->ev.pos);
     228                claim = ui_wdecor_pos_event(awnd->wdecor, &event->ev.pos);
     229                /* Note: If event is claimed, awnd might not be valid anymore */
     230                if (claim == ui_unclaimed)
     231                        ui_window_send_pos(awnd, &event->ev.pos);
    210232                break;
    211233        }
     
    248270}
    249271
     272/** Repaint UI (only used in fullscreen mode).
     273 *
     274 * This is used when an area is exposed in fullscreen mode.
     275 *
     276 * @param ui UI
     277 * @return @c EOK on success or an error code
     278 */
     279errno_t ui_paint(ui_t *ui)
     280{
     281        errno_t rc;
     282        ui_window_t *awnd;
     283
     284        /* XXX Should repaint all windows */
     285        awnd = ui_window_get_active(ui);
     286        if (awnd == NULL)
     287                return EOK;
     288
     289        rc = ui_wdecor_paint(awnd->wdecor);
     290        if (rc != EOK)
     291                return rc;
     292
     293        return ui_window_paint(awnd);
     294}
     295
    250296/** Terminate user interface.
    251297 *
     
    275321}
    276322
     323/** Determine if we are emulating windows.
     324 *
     325 * @param ui User interface
     326 * @return @c true iff we are running in text mode
     327 */
     328bool ui_is_fullscreen(ui_t *ui)
     329{
     330        return (ui->display == NULL);
     331}
     332
    277333/** @}
    278334 */
  • uspace/lib/ui/src/wdecor.c

    rde227aba r252d03c  
    556556 * @param wdecor Window decoration
    557557 * @param pos_event Position event
    558  */
    559 void ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
     558 * @return @c ui_claimed iff event was claimed
     559 */
     560ui_evclaim_t ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
    560561{
    561562        gfx_coord2_t pos;
     
    571572                claim = ui_pbutton_pos_event(wdecor->btn_close, event);
    572573                if (claim == ui_claimed)
    573                         return;
     574                        return ui_claimed;
    574575        }
    575576
    576577        ui_wdecor_frame_pos_event(wdecor, event);
    577578
    578         if ((wdecor->style & ui_wds_titlebar) != 0)  {
     579        if ((wdecor->style & ui_wds_titlebar) != 0) {
    579580                if (event->type == POS_PRESS &&
    580                     gfx_pix_inside_rect(&pos, &geom.title_bar_rect))
     581                    gfx_pix_inside_rect(&pos, &geom.title_bar_rect)) {
    581582                        ui_wdecor_move(wdecor, &pos);
    582         }
     583                        return ui_claimed;
     584                }
     585        }
     586
     587        return ui_unclaimed;
    583588}
    584589
  • uspace/lib/ui/src/window.c

    rde227aba r252d03c  
    133133        gfx_bitmap_t *bmp = NULL;
    134134        mem_gc_t *memgc = NULL;
    135         console_gc_t *cgc = NULL;
    136135        errno_t rc;
    137 
    138         if (ui->root_wnd != NULL)
    139                 return EEXIST;
    140136
    141137        window = calloc(1, sizeof(ui_window_t));
     
    202198                        goto error;
    203199        } else if (ui->console != NULL) {
    204                 rc = console_gc_create(ui->console, NULL, &cgc);
    205                 if (rc != EOK)
    206                         goto error;
    207 
    208                 gc = console_gc_get_ctx(cgc);
     200                gc = console_gc_get_ctx(ui->cgc);
    209201        } else {
    210202                /* Needed for unit tests */
     
    257249        window->gc = gc;
    258250#endif
    259         window->cgc = cgc;
    260251
    261252        rc = ui_resource_create(window->gc, ui_is_textmode(ui), &res);
     
    282273        *rwindow = window;
    283274
    284         ui->root_wnd = window;
     275        list_append(&window->lwindows, &ui->windows);
    285276        return EOK;
    286277error:
     
    295286        if (dgc != NULL)
    296287                dummygc_destroy(dgc);
    297         if (cgc != NULL)
    298                 console_gc_delete(cgc);
    299288        if (dwindow != NULL)
    300289                display_window_destroy(dwindow);
     
    309298void ui_window_destroy(ui_window_t *window)
    310299{
     300        ui_t *ui;
     301
    311302        if (window == NULL)
    312303                return;
    313304
     305        ui = window->ui;
     306
     307        list_remove(&window->lwindows);
    314308        ui_control_destroy(window->control);
    315309        ui_wdecor_destroy(window->wdecor);
     
    328322        if (window->dwindow != NULL)
    329323                display_window_destroy(window->dwindow);
    330         if (window->cgc != NULL)
    331                 console_gc_delete(window->cgc);
     324
    332325        free(window);
     326
     327        /* Need to repaint if windows are emulated */
     328        if (ui_is_fullscreen(ui)) {
     329                ui_paint(ui);
     330        }
    333331}
    334332
     
    362360        window->control = NULL;
    363361        control->elemp = NULL;
     362}
     363
     364/** Get active window (only valid in fullscreen mode).
     365 *
     366 * @param ui User interface
     367 * @return Active window
     368 */
     369ui_window_t *ui_window_get_active(ui_t *ui)
     370{
     371        link_t *link;
     372
     373        link = list_last(&ui->windows);
     374        if (link == NULL)
     375                return NULL;
     376
     377        return list_get_instance(link, ui_window_t, lwindows);
    364378}
    365379
  • uspace/lib/ui/test/ui.c

    rde227aba r252d03c  
    3535PCUT_TEST_SUITE(ui);
    3636
    37 /** Create and destroy UI */
    38 PCUT_TEST(create_destroy)
     37/** Create and destroy UI with display */
     38PCUT_TEST(create_disp_destroy)
    3939{
    4040        ui_t *ui = NULL;
     
    4949}
    5050
     51/** Create and destroy UI with console */
     52PCUT_TEST(create_cons_destroy)
     53{
     54        ui_t *ui = NULL;
     55        errno_t rc;
     56
     57        rc = ui_create_cons(NULL, &ui);
     58        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     59        PCUT_ASSERT_NOT_NULL(ui);
     60        PCUT_ASSERT_NULL(ui->console);
     61
     62        ui_destroy(ui);
     63}
     64
    5165/** ui_destroy() can take NULL argument (no-op) */
    5266PCUT_TEST(destroy_null)
     
    5569}
    5670
     71/** ui_run() / ui_quit() */
     72PCUT_TEST(run_quit)
     73{
     74        ui_t *ui = NULL;
     75        errno_t rc;
     76
     77        rc = ui_create_disp(NULL, &ui);
     78        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     79        PCUT_ASSERT_NOT_NULL(ui);
     80
     81        /* Set exit flag */
     82        ui_quit(ui);
     83
     84        /* ui_run() should return immediately */
     85        ui_run(ui);
     86
     87        ui_destroy(ui);
     88}
     89
     90/** ui_paint() */
     91PCUT_TEST(paint)
     92{
     93        ui_t *ui = NULL;
     94        errno_t rc;
     95
     96        rc = ui_create_cons(NULL, &ui);
     97        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     98        PCUT_ASSERT_NOT_NULL(ui);
     99
     100        /* In absence of windows ui_paint() should just return EOK */
     101        rc = ui_paint(ui);
     102        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     103
     104        ui_destroy(ui);
     105}
     106
     107/** ui_is_textmode() */
     108PCUT_TEST(is_textmode)
     109{
     110        ui_t *ui = NULL;
     111        errno_t rc;
     112
     113        rc = ui_create_disp((display_t *)(-1), &ui);
     114        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     115        PCUT_ASSERT_NOT_NULL(ui);
     116
     117        PCUT_ASSERT_FALSE(ui_is_textmode(ui));
     118
     119        ui_destroy(ui);
     120
     121        rc = ui_create_cons((console_ctrl_t *)(-1), &ui);
     122        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     123        PCUT_ASSERT_NOT_NULL(ui);
     124
     125        PCUT_ASSERT_TRUE(ui_is_textmode(ui));
     126
     127        ui_destroy(ui);
     128}
     129
     130/** ui_is_fullscreen() */
     131PCUT_TEST(is_fullscreen)
     132{
     133        ui_t *ui = NULL;
     134        errno_t rc;
     135
     136        rc = ui_create_disp((display_t *)(-1), &ui);
     137        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     138        PCUT_ASSERT_NOT_NULL(ui);
     139
     140        PCUT_ASSERT_FALSE(ui_is_fullscreen(ui));
     141
     142        ui_destroy(ui);
     143
     144        rc = ui_create_cons((console_ctrl_t *)(-1), &ui);
     145        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     146        PCUT_ASSERT_NOT_NULL(ui);
     147
     148        PCUT_ASSERT_TRUE(ui_is_fullscreen(ui));
     149
     150        ui_destroy(ui);
     151}
     152
    57153PCUT_EXPORT(ui);
  • uspace/lib/ui/test/window.c

    rde227aba r252d03c  
    178178}
    179179
     180/** ui_window_get_active */
     181PCUT_TEST(get_active)
     182{
     183        errno_t rc;
     184        ui_t *ui = NULL;
     185        ui_wnd_params_t params;
     186        ui_window_t *window1 = NULL;
     187        ui_window_t *window2 = NULL;
     188        ui_window_t *awnd;
     189
     190        rc = ui_create_cons(NULL, &ui);
     191        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     192
     193        awnd = ui_window_get_active(ui);
     194        PCUT_ASSERT_NULL(awnd);
     195
     196        ui_wnd_params_init(&params);
     197        params.caption = "Hello";
     198
     199        rc = ui_window_create(ui, &params, &window1);
     200        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     201        PCUT_ASSERT_NOT_NULL(window1);
     202
     203        awnd = ui_window_get_active(ui);
     204        PCUT_ASSERT_EQUALS(window1, awnd);
     205
     206        rc = ui_window_create(ui, &params, &window2);
     207        PCUT_ASSERT_ERRNO_VAL(EOK, rc);
     208        PCUT_ASSERT_NOT_NULL(window2);
     209
     210        awnd = ui_window_get_active(ui);
     211        PCUT_ASSERT_EQUALS(window2, awnd);
     212
     213        ui_window_destroy(window2);
     214
     215        awnd = ui_window_get_active(ui);
     216        PCUT_ASSERT_EQUALS(window1, awnd);
     217
     218        ui_window_destroy(window1);
     219
     220        awnd = ui_window_get_active(ui);
     221        PCUT_ASSERT_NULL(awnd);
     222
     223        ui_destroy(ui);
     224}
     225
    180226/** ui_window_resize */
    181227PCUT_TEST(resize)
Note: See TracChangeset for help on using the changeset viewer.