From ad67467e12244ca877ed42ba83317337a183192b Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Fri, 28 Jun 2013 02:17:27 +0100 Subject: [PATCH] [localization] add subdialog localization --- src/.msvc/rufus.loc | 12 ++- src/localization.c | 178 +++++++++++++++++++++++++++++++++++++------- src/localization.h | 86 ++++++++++++++++++++- src/parser.c | 9 ++- src/rufus.c | 12 ++- src/stdlg.c | 4 + 6 files changed, 267 insertions(+), 34 deletions(-) diff --git a/src/.msvc/rufus.loc b/src/.msvc/rufus.loc index 784c41d0..899c4a9d 100644 --- a/src/.msvc/rufus.loc +++ b/src/.msvc/rufus.loc @@ -48,4 +48,14 @@ t IDC_SET_ICON "创建扩展标签和图标文件" m IDC_ADVANCED -24 0 m IDC_NBPASSES 8 0 m IDC_BOOTTYPE 8 0 -m IDC_SELECT_ISO 6 0 \ No newline at end of file +m IDC_SELECT_ISO 6 0 +t IDC_ABOUT "关于..." +t IDC_LOG "日志" +m IDC_LOG -5 0 +r IDC_LOG +5 0 +t IDCANCEL "关闭" +t IDC_START "开始" +p IDD_ABOUTBOX +t IDC_ABOUT_LICENSE "许可证" +t IDC_ABOUT_UPDATES "更新" +t IDOK "确定" diff --git a/src/localization.c b/src/localization.c index 19f995b0..168ea391 100644 --- a/src/localization.c +++ b/src/localization.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "rufus.h" #include "resource.h" @@ -36,7 +37,18 @@ #define LOC_CTRL(x) { #x, x } // TODO: move this to an autogenerated file -loc_control_id control_id[] = { +const loc_control_id control_id[] = { + // The dialog IDs must come first + LOC_CTRL(IDD_DIALOG), + LOC_CTRL(IDD_ABOUTBOX), + LOC_CTRL(IDD_NOTIFICATION), + LOC_CTRL(IDD_LICENSE), + LOC_CTRL(IDD_ISO_EXTRACT), + LOC_CTRL(IDD_LOG), + LOC_CTRL(IDD_UPDATE_POLICY), + LOC_CTRL(IDD_NEW_VERSION), + LOC_CTRL(IDOK), + LOC_CTRL(IDCANCEL), LOC_CTRL(IDS_DEVICE_TXT), LOC_CTRL(IDS_PARTITION_TYPE_TXT), LOC_CTRL(IDS_FILESYSTEM_TXT), @@ -52,6 +64,27 @@ loc_control_id control_id[] = { LOC_CTRL(IDC_NBPASSES), LOC_CTRL(IDC_BOOTTYPE), LOC_CTRL(IDC_SELECT_ISO), + LOC_CTRL(IDC_ABOUT), + LOC_CTRL(IDC_LOG), + LOC_CTRL(IDC_START), + LOC_CTRL(IDC_ABOUT_LICENSE), + LOC_CTRL(IDC_ABOUT_UPDATES), +}; + +// Have a root loc_cmd that points to parent loc_cmd +// MUST be in the same order as in resource.h, and with IDs in +// consecutive order with no gap, as we'll use IDD_XYZ = IDD_DIALOG to +// locate our index in dialog_active +#define LOC_DLG_LST(x) { x, NULL, {NULL, NULL} } +loc_dlg_list loc_dlg[IDD_NEW_VERSION - IDD_DIALOG + 1] = { + LOC_DLG_LST(IDD_DIALOG), + LOC_DLG_LST(IDD_ABOUTBOX), + LOC_DLG_LST(IDD_NOTIFICATION), + LOC_DLG_LST(IDD_LICENSE), + LOC_DLG_LST(IDD_ISO_EXTRACT), + LOC_DLG_LST(IDD_LOG), + LOC_DLG_LST(IDD_UPDATE_POLICY), + LOC_DLG_LST(IDD_NEW_VERSION), }; /* c control ID (no space, no quotes), s: quoted string, i: 32 bit signed integer, */ @@ -65,7 +98,7 @@ loc_parse parse_cmd[] = { { 'f', LC_FONT, "si" }, { 'd', LC_DIRECTION, "i" }, }; -size_t PARSE_CMD_SIZE = ARRAYSIZE(parse_cmd); +const size_t PARSE_CMD_SIZE = ARRAYSIZE(parse_cmd); int loc_line_nr = 0; char loc_filename[32]; @@ -78,58 +111,149 @@ void free_loc_cmd(loc_cmd* lcmd) free(lcmd); } +void loc_dlg_add(int index, loc_cmd* lcmd) +{ + if ((lcmd == NULL) || (index < 0) || (index >= ARRAYSIZE(loc_dlg))) { + uprintf("loc_dlg_add: invalid parameter\n"); + return; + } + list_add(&lcmd->list, &loc_dlg[index].list); +} + +// TODO: rename this to something_localization() +void free_loc_dlg(void) +{ + size_t i = 0; + loc_cmd *lcmd, *next; + + for (i=0; ilist); + free_loc_cmd(lcmd); + } + } +} + +/* + * We need to initialize the command lists + */ +void init_localization(void) { + size_t i; + + for (i=0; icommand <= LC_TEXT) { // TODO: should always be the case + hCtrl = GetDlgItem(hDlg, lcmd->ctrl_id); + if (hCtrl == NULL) { + // TODO: store the line nr in command so that we can print a better error? + // Would also avoid global in dispatch + loc_line_nr = lcmd->line_nr; + luprintf("control '%s' is not part of dialog '%s'\n", + lcmd->text[0], control_id[dlg_id-IDD_DIALOG].name); + } + } + + switch(lcmd->command) { + // NB: For commands that take an ID, ctrl_id is always a valid index at this stage + case LC_TEXT: + if (hCtrl != NULL) { + SetWindowTextU(hCtrl, lcmd->text[1]); + } + break; + case LC_MOVE: + if (hCtrl != NULL) { + ResizeMoveCtrl(hDlg, hCtrl, lcmd->num[0], lcmd->num[1], 0, 0); + } + break; + case LC_RESIZE: + if (hCtrl != NULL) { + ResizeMoveCtrl(hDlg, hCtrl, 0, 0, lcmd->num[0], lcmd->num[1]); + } + break; + } + } +} + +// Can't use isWindow() against our existing HWND to avoid this call +// as handles are recycled. +void reset_localization(int dlg_id) +{ + loc_dlg[dlg_id-IDD_DIALOG].hDlg = NULL; +} + // TODO: we need to store a revert for every action we execute here, // or do we want to reinstantiate the dialogs? -BOOL execute_loc_cmd(loc_cmd* lcmd) +BOOL dispatch_loc_cmd(loc_cmd* lcmd) { size_t i; - static HWND hParent = NULL; - static char parent_name[128] = "IDD_DIALOG"; // Keep a copy of the parent - HWND hCtrl = NULL; + static int dlg_index = 0; if (lcmd == NULL) return FALSE; - if (hParent == NULL) - hParent = hMainDialog; - // uprintf("cmd #%d: ('%s', '%s') (%d, %d)\n", // lcmd->command, lcmd->text[0], lcmd->text[1], lcmd->num[0], lcmd->num[1]); + if (lcmd->command <= LC_TEXT) { - // Any command before LC_VERSION takes a control ID in text[0] + // Any command up to LC_TEXT takes a control ID in text[0] for (i=0; itext[0], control_id[i].name) == 0) { - hCtrl = GetDlgItem(hParent, control_id[i].id); + lcmd->ctrl_id = control_id[i].id; break; } } - if (hCtrl == NULL) { - luprintf("'%s' is not a member of '%s'\n", lcmd->text[0], parent_name); + if (lcmd->ctrl_id < 0) { + luprintf("unknown control '%s'\n", lcmd->text[0]); + goto err; } } switch(lcmd->command) { + // NB: Form commands that take an ID, ctrl_id is always a valid index at this stage case LC_TEXT: - if (hCtrl != NULL) { - SetWindowTextU(hCtrl, lcmd->text[1]); - } - break; case LC_MOVE: - if (hCtrl != NULL) { - ResizeMoveCtrl(hParent, hCtrl, lcmd->num[0], lcmd->num[1], 0, 0); - } - break; case LC_RESIZE: - if (hCtrl != NULL) { - ResizeMoveCtrl(hParent, hCtrl, 0, 0, lcmd->num[0], lcmd->num[1]); - } + loc_dlg_add(dlg_index, lcmd); break; case LC_PARENT: - // ??? + if ((lcmd->ctrl_id-IDD_DIALOG) > ARRAYSIZE(loc_dlg)) { + luprintf("'%s' is not a dialog ID\n", lcmd->text[0]); + goto err; + } + dlg_index = lcmd->ctrl_id - IDD_DIALOG; + free_loc_cmd(lcmd); + break; + default: + free_loc_cmd(lcmd); break; } return TRUE; - // /!\ lcmd is freed after this call => if text messages need to be stored, they - // must be removed from cmd so that they won't be freed +err: + free_loc_cmd(lcmd); + return FALSE; } diff --git a/src/localization.h b/src/localization.h index 7ed16fb8..897dd882 100644 --- a/src/localization.h +++ b/src/localization.h @@ -18,6 +18,7 @@ */ #include +#include // What we need for localization // # Comment @@ -42,6 +43,74 @@ #define luprint(msg) uprintf("%s(%d): " msg "\n", loc_filename, loc_line_nr) #define luprintf(msg, ...) uprintf("%s(%d): " msg "\n", loc_filename, loc_line_nr, __VA_ARGS__) +/* + * List handling functions (stolen from libusb) + * NB: offsetof() requires '#include ' + */ +struct list_head { + struct list_head *prev, *next; +}; + +/* Get an entry from the list + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" + */ +#define list_entry(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member))) + +/* Get each entry from a list + * pos - A structure pointer has a "member" element + * head - list head + * member - the list_head element in "pos" + * type - the type of the first parameter + */ +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_entry((head)->next, type, member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, type, member)) + + +#define list_for_each_entry_safe(pos, n, head, member, type) \ + for (pos = list_entry((head)->next, type, member), \ + n = list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, type, member)) + +#define list_empty(entry) ((entry)->next == (entry)) + +static __inline void list_init(struct list_head *entry) +{ + entry->prev = entry->next = entry; +} + +static __inline void list_add(struct list_head *entry, struct list_head *head) +{ + entry->next = head->next; + entry->prev = head; + + head->next->prev = entry; + head->next = entry; +} + +static __inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + entry->next = head; + entry->prev = head->prev; + + head->prev->next = entry; + head->prev = entry; +} + +static __inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + entry->next = entry->prev = NULL; +} + + enum loc_command_type { LC_PARENT, LC_MOVE, @@ -55,8 +124,11 @@ enum loc_command_type { typedef struct loc_cmd_struct { int command; + int ctrl_id; + uint32_t line_nr; char* text[2]; int32_t num[2]; + struct list_head list; } loc_cmd; typedef struct loc_parse_struct { @@ -70,10 +142,20 @@ typedef struct loc_control_id_struct { const int id; } loc_control_id; +typedef struct loc_dlg_list_struct { + const int dlg_id; + HWND hDlg; + struct list_head list; +} loc_dlg_list; + loc_parse parse_cmd[]; -size_t PARSE_CMD_SIZE; +const size_t PARSE_CMD_SIZE; int loc_line_nr; char loc_filename[32]; void free_loc_cmd(loc_cmd* lcmd); -BOOL execute_loc_cmd(loc_cmd* lcmd); +BOOL dispatch_loc_cmd(loc_cmd* lcmd); +void init_localization(void); +void apply_localization(int dlg_id, HWND hDlg); +void reset_localization(int dlg_id); +void free_loc_dlg(void); diff --git a/src/parser.c b/src/parser.c index a2f89c07..720b9ff3 100644 --- a/src/parser.c +++ b/src/parser.c @@ -34,6 +34,7 @@ #include "rufus.h" #include "msapi_utf8.h" #include "localization.h" +#include "resource.h" // TODO: remove_me - only needed for IDD_DIALOG static const wchar_t wspace[] = L" \t"; @@ -58,7 +59,9 @@ static loc_cmd* get_loc_cmd(wchar_t wc, wchar_t* wline) { luprint("could not allocate command"); return NULL; } + lcmd->ctrl_id = -1; lcmd->command = parse_cmd[j].cmd; + lcmd->line_nr = loc_line_nr; i = 0; for (k = 0; parse_cmd[j].arg_type[k] != 0; k++) { @@ -147,8 +150,7 @@ static void* get_loc_data_line(wchar_t* wline) lcmd = get_loc_cmd(t, &wline[i]); // TODO: process LC_LOCALE in seek_locale mode // TODO: check return value? - execute_loc_cmd(lcmd); - free_loc_cmd(lcmd); + dispatch_loc_cmd(lcmd); return NULL; } @@ -176,6 +178,8 @@ char* get_loc_data_file(const char* filename) if ((filename == NULL) || (filename[0] == 0)) return NULL; + // TODO: revert previous changes + free_loc_dlg(); loc_line_nr = 0; safe_strcpy(loc_filename, sizeof(loc_filename), filename); wfilename = utf8_to_wchar(filename); @@ -258,6 +262,7 @@ char* get_loc_data_file(const char* filename) } while(1); out: + apply_localization(IDD_DIALOG, hMainDialog); if (fd != NULL) fclose(fd); safe_free(wfilename); diff --git a/src/rufus.c b/src/rufus.c index 2fcfe08e..9f1391b5 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -40,6 +40,7 @@ #include "resource.h" #include "rufus.h" #include "registry.h" +#include "localization.h" /* Redefinitions for WDK and MinGW */ #ifndef PBM_SETSTATE @@ -1465,6 +1466,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA break; case WM_INITDIALOG: + apply_localization(IDD_DIALOG, hDlg); SetUpdateCheck(); // Create the log window (hidden) hLogDlg = CreateDialogA(hMainInstance, MAKEINTRESOURCEA(IDD_LOG), hDlg, (DLGPROC)LogProc); @@ -1889,7 +1891,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine uprintf("*** " APPLICATION_NAME " init ***\n"); - SetThreadLocale(MAKELCID(LANG_FRENCH, SUBLANG_FRENCH)); + // Init localization + init_localization(); + +// TODO: See what happens with this +// SetThreadLocale(MAKELCID(LANG_FRENCH, SUBLANG_FRENCH)); // Reattach the console, if we were started from commandline if (AttachConsole(ATTACH_PARENT_PROCESS) != 0) { @@ -1975,7 +1981,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine SetLGP(FALSE, &existing_key, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", "NoDriveTypeAutorun", 0x9e); // Create the main Window - if ( (hDlg = CreateDialogA(hInstance, MAKEINTRESOURCEA(IDD_DIALOG), NULL, MainCallback)) == NULL ) { + hDlg = CreateDialogA(hInstance, MAKEINTRESOURCEA(IDD_DIALOG), NULL, MainCallback); + if (hDlg == NULL) { MessageBoxU(NULL, "Could not create Window", "DialogBox failure", MB_ICONSTOP); goto out; } @@ -2042,6 +2049,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine out: DestroyAllTooltips(); + free_loc_dlg(); safe_free(iso_path); safe_free(update.download_url); safe_free(update.release_notes); diff --git a/src/stdlg.c b/src/stdlg.c index 24248d9a..1602f295 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -39,6 +39,7 @@ #include "registry.h" #include "resource.h" #include "license.h" +#include "localization.h" /* The following is only available on Vista and later */ #if (_WIN32_WINNT >= 0x0600) @@ -491,6 +492,8 @@ INT_PTR CALLBACK AboutCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lP switch (message) { case WM_INITDIALOG: + // Execute dialog localization + apply_localization(IDD_ABOUTBOX, hDlg); SetTitleBarIcon(hDlg); CenterDialog(hDlg); if (reg_commcheck) @@ -529,6 +532,7 @@ INT_PTR CALLBACK AboutCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lP switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: + reset_localization(IDD_ABOUTBOX); EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; case IDC_ABOUT_LICENSE: