langpackedit v0.13 -- from 8f9f0878
[wdl.git] / WDL / localize / langpack_edit / langpack_edit.cpp
blobef0e5dcfb7bbdbc284c3eb0ffd0c020dc6893494
1 /*
2 LangPackEdit
3 Copyright (C) 2022 Cockos Inc
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 #ifdef _WIN32
23 #include <windows.h>
24 #include <CommCtrl.h>
25 #endif
27 #include "../../swell/swell.h"
29 #include "../../win32_utf8.h"
30 #include "../../wingui/wndsize.h"
31 #include "../../filebrowse.h"
32 #include "../../wdlstring.h"
34 #include "../../assocarray.h"
36 #include "../../localize/localize.h"
37 #include "../../lineparse.h"
39 #define WDL_HASSTRINGS_REWUTF8_HOOK(str, base) \
40 if ((str) > (base)) switch ((str)[0]) { \
41 case 'r': case 'n': case 't': case '0': if ((str)[-1] == '\\') (str)--; break; \
42 case 'd': case 's': case 'f': case 'g': case 'c': case 'u': if ((str)[-1] == '%') (str)--; break; \
44 #include "../../has_strings.h"
46 #include "resource.h"
49 #ifndef HDF_SORTUP
50 #define HDF_SORTUP 0x0400
51 #endif
52 #ifndef HDF_SORTDOWN
53 #define HDF_SORTDOWN 0x0200
54 #endif
56 void ListView_SetHeaderSortArrow(HWND hlist, int col, int dir)
58 HWND hhdr=ListView_GetHeader(hlist);
59 if (!hhdr) return;
60 for (int i=0; i < Header_GetItemCount(hhdr); ++i)
62 HDITEM hi = { HDI_FORMAT, 0, };
63 Header_GetItem(hhdr, i, &hi);
64 hi.fmt &= ~(HDF_SORTUP|HDF_SORTDOWN);
65 if (i == col) hi.fmt |= (dir > 0 ? HDF_SORTUP : HDF_SORTDOWN);
66 Header_SetItem(hhdr, i, &hi);
71 template<class A, class B> static void Restore_ListSelState(HWND hlist, const WDL_TypedBuf<A> *order, const B *st)
73 #ifdef __APPLE__
74 SendMessage(hlist,WM_SETREDRAW,FALSE,0);
75 #endif
76 int x;
77 const int n = order->GetSize();
78 int frow = -1;
79 for (x = 0; x < n; x ++)
81 bool sel = st->Get(order->Get()[x]);
82 if (frow < 0 && sel) frow = x;
83 ListView_SetItemState(hlist, x, sel ? LVIS_SELECTED : 0, LVIS_SELECTED);
85 if (frow >= 0)
86 ListView_EnsureVisible(hlist, frow, FALSE);
87 #ifdef __APPLE__
88 SendMessage(hlist,WM_SETREDRAW,TRUE,0);
89 #endif
92 template<class A, class B> static void Save_ListSelState(HWND hlist, const WDL_TypedBuf<A> *order, B *st)
94 int x;
95 const int n = order->GetSize();
96 for (x = 0; x < n; x ++)
98 if (ListView_GetItemState(hlist,x,LVIS_SELECTED)) st->AddUnsorted(order->Get()[x], true);
100 st->Resort();
105 #if !defined(_WIN32) && !defined(__APPLE__)
106 bool g_quit;
107 #endif
109 HINSTANCE g_hInstance;
110 WDL_FastString g_ini_file;
112 enum {
113 COL_STATE=0, // if we edit these need to edit the IDs of ID_COL_* in resource.h
114 COL_ID,
115 COL_ROW_IDX,
116 COL_TEMPLATE,
117 COL_LOCALIZED,
118 COL_COMMON_LOCALIZED,
119 COL_MAX,
123 struct pack_rec {
124 char *template_str, *pack_str;
126 char *key_desc; // key name + comments
128 int common_idx;
130 static void freeptrs(pack_rec r)
132 free(r.key_desc);
133 free(r.template_str);
134 free(r.pack_str);
138 struct editor_instance {
139 editor_instance() : m_recs(true, pack_rec::freeptrs),
140 m_column_no_searchflags(0), m_sort_col(COL_ID), m_sort_rev(false),
141 m_hwnd(NULL), m_dirty(false) { }
142 ~editor_instance() { }
144 WDL_FastString m_pack_fn;
146 WDL_StringKeyedArray2<pack_rec> m_recs;
147 WDL_TypedBuf<int> m_display_order;
148 int m_column_no_searchflags; // bits for exclude from search per column
149 int m_sort_col;
150 bool m_sort_rev;
152 HWND m_hwnd;
153 bool m_dirty;
155 WDL_WndSizer m_resize;
157 void load_file(const char *filename, bool is_template);
158 void save_file(const char *filename);
160 void cull_recs();
161 void refresh_list();
162 void sort_display_order();
163 void on_sort_change();
165 const char *get_rec_value(const pack_rec *r, const char *k, int w) const
167 __LOCALIZE_LCACHE("(empty)","langpackedit",empt);
168 switch (w)
170 case COL_STATE:
172 if (!r->template_str)
174 if (strstr(k,":5CA1E00000000000")) return "";
175 __LOCALIZE_LCACHE("not-in-template","langpackedit",nit);
176 return nit;
178 if (r->pack_str) return "";
179 if (r->common_idx>=0)
181 const char *k2;
182 pack_rec *r2 = m_recs.EnumeratePtr(r->common_idx,&k2);
183 if (WDL_NORMALLY(r2 && k2))
185 WDL_ASSERT(!strcmp(strstr(k2,":"),strstr(k,":")));
186 __LOCALIZE_LCACHE("localized-in-common","langpackedit",lic);
187 __LOCALIZE_LCACHE("common-not-localized","langpackedit",cnl);
188 if (r2->pack_str) return lic;
189 return cnl;
192 return "not-localized";
194 case COL_ID: return r->key_desc ? r->key_desc : k;
195 case COL_TEMPLATE: return r->template_str;
196 case COL_LOCALIZED: return r->pack_str == NULL ? "" : r->pack_str[0] ? r->pack_str : empt;
197 case COL_COMMON_LOCALIZED:
198 if (r->common_idx>=0)
200 const char *k2;
201 pack_rec *r2 = m_recs.EnumeratePtr(r->common_idx,&k2);
202 if (WDL_NORMALLY(r2 && k2))
204 WDL_ASSERT(!strcmp(strstr(k2,":"),strstr(k,":")));
205 return r2->pack_str == NULL ? "" : r2->pack_str[0] ? r2->pack_str : empt;
208 break;
211 return NULL;
214 const char *get_row_value(int row, int w) const
216 if (row < 0 || row >= m_display_order.GetSize()) return "<err>";
217 const char *k;
218 pack_rec *r = m_recs.EnumeratePtr(m_display_order.Get()[row],&k);
219 if (!r || !k) return "<ERR>";
220 const char *rv = get_rec_value(r,k,w);
221 return rv ? rv : "";
224 bool edit_row(int rec_idx, int other_action=IDC_LOCALIZED_STRING);
225 bool on_key(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
226 void item_context_menu();
228 void set_dirty()
230 if (!m_dirty)
232 m_dirty=true;
233 set_caption();
236 void set_caption()
238 if (m_hwnd)
240 char tmp[512];
241 snprintf(tmp,sizeof(tmp),"%s%sLangPackEdit%s",
242 m_pack_fn.get_filepart(),m_pack_fn.GetLength() ? " - ": "",m_dirty ? __LOCALIZE(" (unsaved)","langpackedit") :"");
243 SetWindowText(m_hwnd,tmp);
246 bool prompt_exit()
248 if (m_dirty)
250 int a = MessageBox(m_hwnd,__LOCALIZE("LangPack is not saved, save before exiting?","langpackedit"),
251 __LOCALIZE("Unsaved","langpackedit"),MB_YESNOCANCEL);
252 if (a == IDCANCEL) return false;
253 if (a == IDNO) m_dirty = false;
254 else SendMessage(m_hwnd,WM_COMMAND,IDC_PACK_SAVE,0);
256 return !m_dirty;
260 static void del_array(WDL_KeyedArray<WDL_UINT64, char *> *d) { delete d; }
262 static void format_section_id(char *buf, size_t bufsz, const char *section, WDL_UINT64 id)
264 snprintf(buf,bufsz,"%s:%08X%08X",section, (int)(id>>32),(int)(id&0xffffffff));
267 static const char *parse_section_id(const char *k, char *buf, int bufsz) // returns ID or NULL
269 if (buf) lstrcpyn_safe(buf, k, bufsz);
271 char *base = buf ? buf : (char*)k, *p = base;
272 while (*p) p++;
273 while (p > base && *p != ':') p--;
274 if (WDL_NOT_NORMALLY(p==base)) return NULL;
275 if (buf) *p=0;
276 return p+1;
279 void editor_instance::save_file(const char *filename)
281 FILE *fp = fopen(filename,"wb");
282 if (!fp)
284 MessageBox(m_hwnd,__LOCALIZE("Error opening file for writing","langpackedit"),
285 __LOCALIZE("Error","langpackedit"),MB_OK);
286 return;
288 char buf[32768];
289 buf[0]=0;
290 if (WDL_NORMALLY(m_hwnd))
291 GetDlgItemText(m_hwnd,IDC_COMMENTS,buf,sizeof(buf));
292 WDL_remove_trailing_whitespace(buf);
293 fprintf(fp,"%s\r\n",buf);
295 char last_sec[1024];
296 last_sec[0]=0;
298 for (int x = 0; x < m_recs.GetSize(); x ++)
300 const char *k;
301 const pack_rec *rec = m_recs.EnumeratePtr(x,&k);
302 if (!rec->pack_str) continue;
304 char sec[256];
305 const char *id = parse_section_id(k,sec,sizeof(sec));
306 if (WDL_NORMALLY(id))
308 if (stricmp(last_sec,sec))
310 lstrcpyn_safe(last_sec,sec,sizeof(last_sec));
311 const char *trail = "";
312 if (rec->key_desc)
314 trail = strstr(rec->key_desc," ");
315 trail = trail ? (trail+1) : "";
317 fprintf(fp,"\r\n[%s]%s\r\n",sec,trail);
319 fprintf(fp,"%s=%s\r\n",id,rec->pack_str);
322 fclose(fp);
325 void editor_instance::load_file(const char *filename, bool is_template)
327 for (int x = 0; x < m_recs.GetSize(); x ++)
329 pack_rec *r = m_recs.EnumeratePtr(x);
330 if (is_template)
332 free(r->template_str);
333 r->template_str = NULL;
335 else
337 free(r->pack_str);
338 r->pack_str = NULL;
342 WDL_StringKeyedArray<char *> extra(true, WDL_StringKeyedArray<char>::freecharptr);
343 if (*filename)
345 WDL_StringKeyedArray< WDL_KeyedArray<WDL_UINT64, char *> * > r(false,del_array);
346 WDL_LoadLanguagePackInternal(filename,&r,NULL, is_template, true, &extra);
348 for (int si = 0; si < r.GetSize(); si ++)
350 const char *sec_name;
351 WDL_KeyedArray<WDL_UINT64, char *> *sec = r.Enumerate(si,&sec_name);
352 for (int i = 0; i < sec->GetSize(); i ++)
354 WDL_UINT64 id;
355 const char *value = sec->Enumerate(i,&id);
356 if (WDL_NOT_NORMALLY(!value)) break;
358 char rec_buf[256], key_desc[256];
359 format_section_id(rec_buf,sizeof(rec_buf),sec_name, id);
362 const char *p = extra.Get(sec_name);
363 if (p)
364 snprintf(key_desc,sizeof(key_desc),"%.100s %s",rec_buf,p);
365 else
366 key_desc[0]=0;
369 pack_rec *rec = m_recs.GetPtr(rec_buf);
370 if (!rec)
372 pack_rec newr = { 0 };
373 if (is_template) newr.template_str = strdup(value);
374 else newr.pack_str = strdup(value);
376 if (key_desc[0]) newr.key_desc = strdup(key_desc);
377 m_recs.Insert(rec_buf,newr);
379 else
381 if (is_template || key_desc[0])
383 free(rec->key_desc);
384 rec->key_desc = key_desc[0] ? strdup(key_desc) : NULL;
386 if (is_template)
388 free(rec->template_str);
389 rec->template_str = strdup(value);
391 else
393 free(rec->pack_str);
394 rec->pack_str = strdup(value);
400 cull_recs();
401 refresh_list();
403 if (m_hwnd)
405 SetDlgItemText(m_hwnd,is_template ? IDC_TEMPLATE : IDC_PACK, *filename ? filename : __LOCALIZE("(none)","langpackedit"));
407 if (!is_template)
409 WDL_FastString fs;
410 for (int x=0; ; x++)
412 char tmp[512];
413 snprintf(tmp,sizeof(tmp),"_initial_comment_%d",x);
414 const char *p = extra.Get(tmp);
415 if (!p) break;
416 if (fs.GetLength()) fs.Append("\r\n");
417 fs.Append(p);
419 if (fs.GetLength())
420 WDL_remove_trailing_whitespace((char *)fs.Get());
421 SetDlgItemText(m_hwnd,IDC_COMMENTS,fs.Get());
426 void editor_instance::cull_recs()
428 for (int x = 0; x < m_recs.GetSize(); x ++)
430 pack_rec *r = m_recs.EnumeratePtr(x);
431 if (!r->template_str && !r->pack_str)
432 m_recs.DeleteByIndex(x--);
436 void editor_instance::refresh_list()
438 HWND list = WDL_NORMALLY(m_hwnd) ? GetDlgItem(m_hwnd,IDC_LIST) : NULL;
439 WDL_IntKeyedArray<bool> selState;
440 if (list)
441 Save_ListSelState(list, &m_display_order, &selState);
443 m_display_order.Resize(0,false);
445 LineParser lp;
446 if (m_hwnd)
448 char filter[512];
449 GetDlgItemText(m_hwnd,IDC_FILTER,filter,sizeof(filter));
450 WDL_makeSearchFilter(filter, &lp);
452 const bool do_filt = lp.getnumtokens()>0;
453 for (int x = 0; x < m_recs.GetSize(); x ++)
455 const char *k = NULL;
456 pack_rec *r = m_recs.EnumeratePtr(x,&k);
457 if (WDL_NOT_NORMALLY(!r)) break;
459 if (strnicmp(k,"common:",7))
461 const char *sid = parse_section_id(k,NULL,0);
462 if (WDL_NORMALLY(sid))
464 char tmp[256];
465 snprintf(tmp,sizeof(tmp),"common:%s",sid);
466 r->common_idx = m_recs.GetIdx(tmp);
469 else
470 r->common_idx = -1;
472 const char *strs[COL_MAX];
473 int nc = 0;
474 if (do_filt)
476 for (int c =0; c < COL_MAX; c ++)
478 if (m_column_no_searchflags & (1<<c)) continue;
479 const char *v = get_rec_value(r,k,c);
480 if (v) strs[nc++] = v;
484 if (!do_filt || WDL_hasStringsEx2(strs,nc,&lp))
485 m_display_order.Add(&x,1);
488 sort_display_order();
490 if (list)
492 ListView_SetItemCount(list, m_display_order.GetSize());
493 Restore_ListSelState(list, &m_display_order, &selState);
494 ListView_RedrawItems(list, 0, m_display_order.GetSize());
498 static editor_instance *sort_inst;
499 static int sort_func(const void *a, const void *b)
501 const int idx_a = *(const int *)a, idx_b = *(const int *)b;
502 int ret = idx_a < idx_b ? -1 : idx_a > idx_b ? 1 : 0;
504 const int col = sort_inst->m_sort_col;
505 switch (col)
507 case COL_STATE:
508 case COL_TEMPLATE:
509 case COL_LOCALIZED:
510 case COL_COMMON_LOCALIZED:
511 case COL_ID:
513 const char *ak, *bk;
514 const pack_rec *ar = sort_inst->m_recs.EnumeratePtr(idx_a,&ak);
515 const pack_rec *br = sort_inst->m_recs.EnumeratePtr(idx_b,&bk);
516 const char *av = ar ? sort_inst->get_rec_value(ar,ak,col) : NULL;
517 const char *bv = br ? sort_inst->get_rec_value(br,bk,col) : NULL;
518 if (av || bv)
520 if (av) while (*av == ' ') av++;
521 if (bv) while (*bv == ' ') bv++;
522 if (col == COL_ID) ret = stricmp(av?av:"",bv?bv:"");
523 else ret = WDL_strcmp_logical_ex(av?av:"",bv?bv:"",0,WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT);
526 break;
528 return sort_inst->m_sort_rev ? -ret : ret;
532 void editor_instance::on_sort_change()
534 char tmp[64];
535 snprintf(tmp, sizeof(tmp), "%d", m_sort_col);
536 WritePrivateProfileString("LangPackEdit", "sortcol", tmp, g_ini_file.Get());
537 WritePrivateProfileString("LangPackEdit", "sortrev", m_sort_rev ? "1" : "0", g_ini_file.Get());
539 if (!m_hwnd) return;
540 HWND list = GetDlgItem(m_hwnd,IDC_LIST);
542 if (!list) return;
543 WDL_IntKeyedArray<bool> selState;
544 Save_ListSelState(list, &m_display_order, &selState);
546 sort_display_order();
548 ListView_SetHeaderSortArrow(list, m_sort_col, (m_sort_rev ? -1 : 1));
549 ListView_SetItemCount(list, m_display_order.GetSize());
550 Restore_ListSelState(list, &m_display_order, &selState);
552 ListView_RedrawItems(list, 0, m_display_order.GetSize());
555 void editor_instance::sort_display_order()
557 if (m_display_order.GetSize() > 1)
559 sort_inst = this;
560 qsort(m_display_order.Get(), m_display_order.GetSize(), sizeof(m_display_order.Get()[0]), sort_func);
561 sort_inst = NULL;
565 WDL_DLGRET editorProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
567 switch (uMsg)
569 case WM_INITDIALOG:
570 SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
572 void **p = (void **)lParam;
573 editor_instance *edit = (editor_instance *)p[0];
574 int item = (int) (INT_PTR) p[1];
575 const char *k;
576 pack_rec *rec = edit->m_recs.EnumeratePtr(item,&k);
577 if (WDL_NORMALLY(rec))
579 char tmp[512];
580 snprintf(tmp,sizeof(tmp),__LOCALIZE_VERFMT("Localize: %s","langpackedit"),k);
581 SetWindowText(hwndDlg,tmp);
583 if (rec->template_str)
584 SetDlgItemText(hwndDlg,IDC_TEMPLATE_STRING,rec->template_str);
586 const char *common_str = NULL;
587 if (rec->common_idx>=0)
589 const char *k2;
590 pack_rec *r2 = edit->m_recs.EnumeratePtr(rec->common_idx,&k2);
591 if (WDL_NORMALLY(r2 && k2))
593 WDL_ASSERT(!strcmp(strstr(k2,":"),strstr(k,":")));
594 SetDlgItemText(hwndDlg,IDC_COMMON_STRING,r2->pack_str ? r2->pack_str : __LOCALIZE("(not yet localized)","langpackedit"));
595 common_str = r2->pack_str;
598 else if (!strnicmp(k,"common:",7))
600 ShowWindow(GetDlgItem(hwndDlg,IDC_COMMON_LABEL),SW_HIDE);
601 ShowWindow(GetDlgItem(hwndDlg,IDC_COMMON_STRING),SW_HIDE);
603 else
604 SetDlgItemText(hwndDlg,IDC_COMMON_STRING,__LOCALIZE("(not in [common])","langpackedit"));
606 if (rec->pack_str || rec->template_str)
607 SetDlgItemText(hwndDlg,IDC_LOCALIZED_STRING,rec->pack_str ? rec->pack_str :
608 common_str ? common_str : rec->template_str);
611 return 0;
612 case WM_COMMAND:
613 switch (LOWORD(wParam))
615 case IDC_REMOVE_LOCALIZATION:
616 case IDC_COPY_TEMPLATE:
617 case IDOK:
619 void **p = (void **)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
620 editor_instance *edit = (editor_instance *)p[0];
621 int item = (int) (INT_PTR) p[1];
622 const char *k;
623 pack_rec *rec = edit->m_recs.EnumeratePtr(item,&k);
624 if (WDL_NORMALLY(rec))
626 if (LOWORD(wParam) == IDOK)
628 char buf[32768];
629 GetDlgItemText(hwndDlg,IDC_LOCALIZED_STRING,buf,sizeof(buf));
630 free(rec->pack_str);
631 rec->pack_str = strdup(buf);
633 else
635 free(rec->pack_str);
636 rec->pack_str = LOWORD(wParam) == IDC_COPY_TEMPLATE && rec->template_str ?
637 strdup(rec->template_str) : NULL;
641 EndDialog(hwndDlg,1);
642 break;
643 case IDCANCEL:
644 EndDialog(hwndDlg,0);
645 break;
647 break;
649 return 0;
652 bool editor_instance::edit_row(int row, int other_action)
654 if (row < 0 || row >= m_display_order.GetSize()) return false;
655 int rec_idx = m_display_order.Get()[row];
657 WDL_ASSERT(rec_idx>=0 && rec_idx < m_recs.GetSize());
659 if (other_action && other_action != IDC_LOCALIZED_STRING)
661 const char *k;
662 pack_rec *r = m_recs.EnumeratePtr(rec_idx,&k);
663 if (WDL_NORMALLY(r))
665 switch (other_action)
667 case IDC_COMMON_STRING:
668 if (r->common_idx>=0 && r->common_idx < m_recs.GetSize())
669 rec_idx = r->common_idx;
670 break;
671 case IDC_REMOVE_NONLOCALIZATION:
672 if (r->pack_str)
674 if (!r->template_str) return false;
675 if (strcmp(r->template_str,r->pack_str)) return false;
677 // fall through
678 case IDC_REMOVE_LOCALIZATION:
679 case IDC_COPY_TEMPLATE:
680 if (other_action == IDC_COPY_TEMPLATE && r->pack_str) return false; // do not modify already-localized strings
682 free(r->pack_str);
683 r->pack_str = other_action == IDC_COPY_TEMPLATE && r->template_str ? strdup(r->template_str) : NULL;
684 return true;
685 case ID_SCALING_ADD:
687 char sec[256];
688 if (WDL_NORMALLY(parse_section_id(k,sec,sizeof(sec))))
690 lstrcatn(sec,":5CA1E00000000000",sizeof(sec));
691 if (!m_recs.GetPtr(sec))
693 pack_rec newr = { 0 };
694 m_recs.Insert(sec,newr);
695 int idx = m_recs.GetIdx(sec);
696 if (WDL_NORMALLY(idx>=0))
698 // added a rec, adjust indices and stick us at the end
699 for (int x = 0; x < m_display_order.GetSize(); x ++)
701 if (m_display_order.Get()[x] >= idx)
702 m_display_order.Get()[x]++;
704 m_display_order.Add(idx);
705 return true;
711 return false;
716 void *p[2] = { this, (void *)(INT_PTR) rec_idx };
717 if (DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_RENAME), m_hwnd, editorProc, (LPARAM)p))
719 refresh_list();
720 set_dirty();
721 return true;
723 return false;
727 bool editor_instance::on_key(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
729 if (!m_hwnd || (hwnd != m_hwnd && !IsChild(m_hwnd,hwnd)))
730 return false;
732 #ifndef __APPLE__
733 if (msg == WM_KEYDOWN && (wParam=='S' || wParam == 'O' || wParam == 'T'))
735 bool shift = !!(GetAsyncKeyState(VK_SHIFT)&0x8000);
736 bool ctrl = !!(GetAsyncKeyState(VK_CONTROL)&0x8000);
737 if (!shift && ctrl)
739 bool alt = !!(GetAsyncKeyState(VK_MENU)&0x8000);
740 int cmd = 0;
741 if (wParam == 'S' && ctrl) cmd = alt ? IDC_PACK_SAVE_AS : IDC_PACK_SAVE;
742 else if (wParam == 'O') cmd = IDC_PACK_LOAD;
743 else if (wParam == 'T') cmd = IDC_TEMPLATE_LOAD;
745 if (cmd)
747 SendMessage(m_hwnd,WM_COMMAND,cmd,0);
748 return 1;
752 #endif
754 HWND hlist = GetDlgItem(m_hwnd,IDC_LIST);
755 if (hwnd == hlist || IsChild(hlist,hwnd))
757 if (msg == WM_KEYDOWN) switch (wParam)
759 case VK_RETURN:
760 SendMessage(m_hwnd,WM_COMMAND,IDC_LOCALIZED_STRING,0);
761 return true;
762 break;
763 #ifdef _WIN32
764 case VK_APPS:
765 item_context_menu();
766 return true;
767 #endif
768 case VK_BACK:
769 case VK_DELETE:
770 SendMessage(m_hwnd,WM_COMMAND,IDC_REMOVE_LOCALIZATION,0);
771 return true;
774 return false;
777 void editor_instance::item_context_menu()
779 HMENU menu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_CONTEXTMENU));
780 POINT p;
781 GetCursorPos(&p);
782 TrackPopupMenu(GetSubMenu(menu,0),0,p.x,p.y,0,m_hwnd,NULL);
783 DestroyMenu(menu);
786 const char *COL_DESCS[COL_MAX] = {
787 // !WANT_LOCALIZE_STRINGS_BEGIN:langpackedit
788 "State",
789 "ID",
790 "Row",
791 "Template",
792 "Localized",
793 "Common Localized",
794 // !WANT_LOCALIZE_STRINGS_END
797 int COL_SIZES[COL_MAX] = {
798 120,
799 120,
801 240,
802 240,
803 240,
806 editor_instance g_editor;
807 WDL_DLGRET mainProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
809 enum {
810 TIMER_FILTER=1
812 switch (uMsg)
814 case WM_INITDIALOG:
815 g_editor.m_hwnd = hwndDlg;
816 #ifdef _WIN32
818 HICON icon=LoadIcon(g_hInstance,MAKEINTRESOURCE(IDI_ICON1));
819 SetClassLongPtr(hwndDlg,GCLP_HICON,(LPARAM)icon);
821 #endif
823 g_editor.m_resize.init(hwndDlg);
824 g_editor.m_resize.init_item(IDC_FILTER,0,0,1,0);
825 g_editor.m_resize.init_item(IDC_TEMPLATE,0,0,1,0);
826 g_editor.m_resize.init_item(IDC_PACK,0,0,1,0);
827 g_editor.m_resize.init_item(IDC_COMMENTS,0,0,1,0);
828 g_editor.m_resize.init_item(IDC_LIST,0,0,1,1);
831 HWND hlist = GetDlgItem(hwndDlg, IDC_LIST);
833 int s=LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_HEADERDRAGDROP;
834 #ifdef _WIN32
835 s|=LVS_EX_DOUBLEBUFFER;
836 #endif
837 ListView_SetExtendedListViewStyleEx(hlist, s,s );
839 #ifdef _WIN32
840 WDL_UTF8_HookListView(hlist);
841 SendMessage(hlist,LVM_SETUNICODEFORMAT,1,0);
842 #endif
843 int colorder[COL_MAX];
844 for (int x = 0; x < COL_MAX; x ++)
846 char buf[64];
847 sprintf(buf, "colwid_%d", x);
848 int colw=GetPrivateProfileInt("LangPackEdit", buf, COL_SIZES[x], g_ini_file.Get());
849 sprintf(buf, "colorder_%d", x);
850 colorder[x]=GetPrivateProfileInt("LangPackEdit", buf, x, g_ini_file.Get());
852 LVCOLUMN lvc = { LVCF_TEXT|LVCF_WIDTH, 0, colw, (char*)__localizeFunc(COL_DESCS[x],"langpackedit",LOCALIZE_FLAG_NOCACHE) };
853 ListView_InsertColumn(hlist, x, &lvc);
855 ListView_SetColumnOrderArray(hlist, COL_MAX, colorder);
859 char buf[2048];
860 GetPrivateProfileString("LangPackEdit","template","",buf,sizeof(buf),g_ini_file.Get());
861 g_editor.load_file(buf,true);
863 GetPrivateProfileString("LangPackEdit","lastpack","",buf,sizeof(buf),g_ini_file.Get());
864 g_editor.load_file(buf,false);
866 g_editor.m_column_no_searchflags = GetPrivateProfileInt("LangPackEdit","nosearchcols",0,g_ini_file.Get());
867 g_editor.m_sort_col = GetPrivateProfileInt("LangPackEdit","sortcol",COL_ID,g_ini_file.Get());
868 g_editor.m_sort_rev = GetPrivateProfileInt("LangPackEdit","sortrev",0,g_ini_file.Get()) > 0;
869 ListView_SetHeaderSortArrow(GetDlgItem(hwndDlg,IDC_LIST), g_editor.m_sort_col, (g_editor.m_sort_rev ? -1 : 1));
870 g_editor.m_pack_fn.Set(buf);
871 g_editor.set_caption();
873 return 1;
874 case WM_DESTROY:
877 HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
878 int colorder[COL_MAX];
879 for (int i=0; i < COL_MAX; ++i) colorder[i]=i;
880 ListView_GetColumnOrderArray(hlist, COL_MAX, colorder);
882 for (int i=0; i < COL_MAX; ++i)
884 int colw = ListView_GetColumnWidth(hlist, i);
885 char buf[256], buf2[256];
886 sprintf(buf2, "colwid_%d", i);
887 sprintf(buf, "%d", colw);
888 char* p = colw == (int)COL_SIZES[i] ? NULL : buf;
889 WritePrivateProfileString("LangPackEdit", buf2, p, g_ini_file.Get());
891 sprintf(buf2, "colorder_%d", i);
892 sprintf(buf, "%d", colorder[i]);
893 p = colorder[i] == i ? NULL : buf;
894 WritePrivateProfileString("LangPackEdit", buf2, p, g_ini_file.Get());
898 g_editor.m_hwnd = NULL;
899 #ifdef __APPLE__
900 SWELL_PostQuitMessage(0);
901 #elif defined(_WIN32)
902 PostQuitMessage(0);
903 #else
904 g_quit = true;
905 #endif
906 break;
907 case WM_SIZE:
908 if (wParam != SIZE_MINIMIZED)
909 g_editor.m_resize.onResize();
910 break;
911 case WM_TIMER:
912 switch (wParam)
914 case TIMER_FILTER:
915 KillTimer(hwndDlg,TIMER_FILTER);
916 g_editor.refresh_list();
917 break;
919 break;
920 case WM_CLOSE:
921 if (g_editor.prompt_exit())
922 DestroyWindow(hwndDlg);
923 break;
924 case WM_COMMAND:
925 switch (LOWORD(wParam))
927 case ID_QUIT:
928 if (g_editor.prompt_exit())
929 DestroyWindow(hwndDlg);
930 break;
931 case IDC_FILTER:
932 if (HIWORD(wParam) == EN_CHANGE)
934 SetTimer(hwndDlg,TIMER_FILTER,100,NULL);
936 break;
937 case IDC_PACK_SAVE_AS:
938 case IDC_PACK_SAVE:
940 if (!g_editor.m_pack_fn.GetLength() || LOWORD(wParam) == IDC_PACK_SAVE_AS)
942 char newfn[2048];
943 if (!WDL_ChooseFileForSave(hwndDlg, __LOCALIZE("Save LangPack as...","langpackedit"),
944 NULL,
945 g_editor.m_pack_fn.Get(),
946 "All files (*.*)\0*.*\0",
948 false,
949 newfn,sizeof(newfn)) || !newfn[0]) return 0;
951 g_editor.m_pack_fn.Set(newfn);
952 WritePrivateProfileString("LangPackEdit","lastpack",newfn,g_ini_file.Get());
953 SetDlgItemText(hwndDlg,IDC_PACK,newfn);
956 g_editor.save_file(g_editor.m_pack_fn.Get());
957 g_editor.m_dirty=false;
958 g_editor.set_caption();
959 break;
961 case IDC_PACK_LOAD:
962 case IDC_TEMPLATE_LOAD:
964 char buf[2048];
965 const bool is_template = LOWORD(wParam) == IDC_TEMPLATE_LOAD;
966 const char *inikey = is_template ? "template" : "lastpack";
967 GetPrivateProfileString("LangPackEdit",inikey,"",buf,sizeof(buf),g_ini_file.Get());
968 char *f = WDL_ChooseFileForOpen(hwndDlg,
969 is_template ? __LOCALIZE("Choose LangPack Template","langpackedit") :
970 __LOCALIZE("Load LangPack","langpackedit"),
971 NULL,
972 buf,
973 "All files (*.*)\0*.*\0"
976 false, false);
977 if (f)
979 WritePrivateProfileString("LangPackEdit",inikey,f,g_ini_file.Get());
980 g_editor.load_file(f,is_template);
981 if (!is_template) g_editor.m_pack_fn.Set(f);
982 free(f);
983 if (!is_template)
985 g_editor.m_dirty=false;
986 g_editor.set_caption();
990 break;
991 case IDC_LOCALIZED_STRING:
992 case IDC_COMMON_STRING:
993 case ID_SCALING_ADD:
994 case IDC_COPY_TEMPLATE:
995 case IDC_REMOVE_LOCALIZATION:
996 case IDC_REMOVE_NONLOCALIZATION:
998 HWND list = GetDlgItem(hwndDlg,IDC_LIST);
999 int cnt = 0;
1000 const int n = ListView_GetItemCount(list);
1001 for (int x = 0; x < n; x ++)
1003 if (ListView_GetItemState(list,x,LVIS_SELECTED) & LVIS_SELECTED)
1005 if (g_editor.edit_row(x,(int)wParam)) cnt++;
1006 if (wParam == ID_SCALING_ADD)
1007 ListView_SetItemState(list,x,0,LVIS_SELECTED);
1010 if (cnt && wParam != IDC_LOCALIZED_STRING)
1012 g_editor.refresh_list();
1013 g_editor.set_dirty();
1015 if (wParam == ID_SCALING_ADD)
1017 const int nn = ListView_GetItemCount(list);
1018 if (n < nn)
1020 ListView_EnsureVisible(list,n,false);
1021 for (int i = n; i < nn; i ++)
1022 ListView_SetItemState(list,i,LVIS_SELECTED,LVIS_SELECTED);
1026 break;
1027 case ID_COL_STATE:
1028 case ID_COL_ID:
1029 case ID_COL_TEMPLATE:
1030 case ID_COL_LOCALIZED:
1031 case ID_COL_COMMONLOCALIZED:
1032 g_editor.m_column_no_searchflags ^= (1<<(LOWORD(wParam) - ID_COL_STATE));
1034 char tmp[64];
1035 snprintf(tmp,sizeof(tmp),"%d",g_editor.m_column_no_searchflags);
1036 WritePrivateProfileString("LangPackEdit","nosearchcols",tmp,g_ini_file.Get());
1038 SetTimer(hwndDlg,TIMER_FILTER,100,NULL);
1039 break;
1040 case ID_SORTCOL_STATE:
1041 case ID_SORTCOL_ID:
1042 case ID_SORTCOL_TEMPLATE:
1043 case ID_SORTCOL_LOCALIZED:
1044 case ID_SORTCOL_COMMONLOCALIZED:
1045 g_editor.m_sort_rev = false;
1046 g_editor.m_sort_col = LOWORD(wParam) - ID_SORTCOL_STATE;
1047 g_editor.on_sort_change();
1048 break;
1049 case ID_SORTCOL_REVERSE:
1050 g_editor.m_sort_rev = !g_editor.m_sort_rev;
1051 g_editor.on_sort_change();
1052 break;
1054 break;
1055 case WM_INITMENUPOPUP:
1056 if (wParam)
1058 HMENU menu = (HMENU) wParam;
1059 static const unsigned short tab[]={
1060 IDC_LOCALIZED_STRING,
1061 IDC_COMMON_STRING,
1062 ID_SCALING_ADD,
1063 IDC_COPY_TEMPLATE,
1064 IDC_REMOVE_LOCALIZATION,
1065 IDC_REMOVE_NONLOCALIZATION,
1067 bool en = ListView_GetSelectedCount(GetDlgItem(hwndDlg,IDC_LIST))>0;
1068 for (size_t x = 0; x < sizeof(tab)/sizeof(tab[0]); x ++)
1069 EnableMenuItem(menu,tab[x],MF_BYCOMMAND|(en ? 0 : MF_GRAYED));
1070 for (int x = 0; x < COL_MAX; x ++)
1071 CheckMenuItem(menu,ID_COL_STATE+x, MF_BYCOMMAND | ((g_editor.m_column_no_searchflags&(1<<x)) ? MF_UNCHECKED:MF_CHECKED));
1072 for (int x = 0; x < COL_MAX; x ++)
1073 CheckMenuItem(menu,ID_SORTCOL_STATE+x, MF_BYCOMMAND | ((g_editor.m_sort_col == x) ? MF_CHECKED:MF_UNCHECKED));
1074 CheckMenuItem(menu,ID_SORTCOL_REVERSE, MF_BYCOMMAND | (g_editor.m_sort_rev ? MF_CHECKED:MF_UNCHECKED));
1076 break;
1077 case WM_NOTIFY:
1079 NMLISTVIEW* lv = (NMLISTVIEW*)lParam;
1080 if (lv->hdr.idFrom == IDC_LIST) switch (lv->hdr.code)
1082 case NM_DBLCLK:
1083 g_editor.edit_row(lv->iItem);
1084 return 0;
1085 case NM_RCLICK:
1086 if (ListView_GetSelectedCount(lv->hdr.hwndFrom)>0)
1088 g_editor.item_context_menu();
1090 return 0;
1091 case LVN_COLUMNCLICK:
1093 int col = lv->iSubItem;
1094 if (col == COL_ROW_IDX) col = COL_ID;
1095 if (g_editor.m_sort_col == col)
1097 g_editor.m_sort_rev = !g_editor.m_sort_rev;
1099 else
1101 g_editor.m_sort_rev = false;
1102 g_editor.m_sort_col = col;
1104 g_editor.on_sort_change();
1106 return 0;
1107 case LVN_GETDISPINFO:
1108 #ifdef _WIN32
1109 case LVN_GETDISPINFOW:
1110 #endif
1112 NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam;
1113 if (lpdi->item.mask & LVIF_TEXT)
1115 if (lpdi->item.iSubItem == COL_ROW_IDX)
1116 snprintf(lpdi->item.pszText,lpdi->item.cchTextMax,"%d",lpdi->item.iItem+1);
1117 else
1118 lpdi->item.pszText = (char*) g_editor.get_row_value(lpdi->item.iItem, lpdi->item.iSubItem);
1119 #ifdef _WIN32
1120 if (lv->hdr.code == LVN_GETDISPINFOW)
1121 WDL_UTF8_ListViewConvertDispInfoToW(lpdi);
1122 #endif
1127 break;
1129 return 0;
1132 INT_PTR SWELLAppMain(int msg, INT_PTR parm1, INT_PTR parm2)
1134 switch (msg)
1136 case SWELLAPP_ONLOAD:
1139 break;
1140 case SWELLAPP_LOADED:
1142 char buf[2048];
1143 GetModuleFileName(NULL,buf,sizeof(buf));
1144 WDL_remove_filepart(buf);
1145 lstrcatn(buf,WDL_DIRCHAR_STR "LangPackEdit.ini",sizeof(buf));
1146 g_ini_file.Set(buf);
1148 WDL_remove_filepart(buf);
1149 lstrcatn(buf,WDL_DIRCHAR_STR "LangPackEdit.LangPack",sizeof(buf));
1151 #ifdef _DEBUG
1152 extern bool g_debug_langpack_has_loaded;
1153 g_debug_langpack_has_loaded=true;
1154 #endif
1155 WDL_LoadLanguagePack(buf,NULL);
1157 HWND h=CreateDialog(NULL,MAKEINTRESOURCE(IDD_DIALOG1),NULL,mainProc);
1158 ShowWindow(h,SW_SHOW);
1160 #ifndef _WIN32
1162 HMENU menu = LoadMenu(NULL,MAKEINTRESOURCE(IDR_MENU1));
1163 #ifdef __APPLE__
1165 HMENU sm=GetSubMenu(menu,0);
1166 DeleteMenu(sm,ID_QUIT,MF_BYCOMMAND); // remove QUIT from our file menu, since it is in the system menu on OSX
1168 // remove any trailing separators
1169 int a= GetMenuItemCount(sm);
1170 while (a > 0 && GetMenuItemID(sm,a-1)==0) DeleteMenu(sm,--a,MF_BYPOSITION);
1173 extern HMENU SWELL_app_stocksysmenu;
1174 if (SWELL_app_stocksysmenu) // insert the stock system menu
1176 HMENU nm=SWELL_DuplicateMenu(SWELL_app_stocksysmenu);
1177 if (nm)
1179 MENUITEMINFO mi={sizeof(mi),MIIM_STATE|MIIM_SUBMENU|MIIM_TYPE,MFT_STRING,0,0,nm,NULL,NULL,0,(char*)"LangPackEdit"};
1180 InsertMenuItem(menu,0,TRUE,&mi);
1183 SetMenuItemModifier(menu,IDC_TEMPLATE_LOAD,MF_BYCOMMAND,'T',FCONTROL);
1184 SetMenuItemModifier(menu,IDC_PACK_LOAD,MF_BYCOMMAND,'O',FCONTROL);
1185 SetMenuItemModifier(menu,IDC_PACK_SAVE,MF_BYCOMMAND,'S',FCONTROL);
1186 SetMenuItemModifier(menu,IDC_PACK_SAVE_AS,MF_BYCOMMAND,'S',FCONTROL|FALT);
1187 #endif
1189 SetMenu(h,menu);
1191 #endif
1194 break;
1195 case SWELLAPP_DESTROY:
1196 if (g_editor.m_hwnd) DestroyWindow(g_editor.m_hwnd);
1197 break;
1198 case SWELLAPP_SHOULDDESTROY:
1199 return g_editor.prompt_exit() ? 0 : 1;
1200 case SWELLAPP_ONCOMMAND:
1201 if (g_editor.m_hwnd)
1202 SendMessage(g_editor.m_hwnd,WM_COMMAND,parm1,0);
1203 return 0;
1204 case SWELLAPP_PROCESSMESSAGE:
1205 if (parm1)
1207 const MSG *m = (MSG *)parm1;
1208 if (m->message == WM_KEYDOWN && m->hwnd)
1210 #ifndef __APPLE__
1211 if (m->wParam == 'Q' &&
1212 (GetAsyncKeyState(VK_CONTROL)&0x8000) &&
1213 !(GetAsyncKeyState(VK_SHIFT)&0x8000) &&
1214 !(GetAsyncKeyState(VK_MENU)&0x8000))
1216 if (g_editor.m_hwnd)
1217 SendMessage(g_editor.m_hwnd,WM_COMMAND,ID_QUIT,0);
1218 return 1;
1220 #endif
1221 if (g_editor.on_key(m->hwnd, m->message, m->wParam, m->lParam))
1222 return 1;
1225 return 0;
1227 return 0;
1232 #ifdef _WIN32
1234 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
1236 g_hInstance = hInstance;
1238 SWELLAppMain(SWELLAPP_ONLOAD,0,0);
1239 SWELLAppMain(SWELLAPP_LOADED,0,0);
1241 for(;;)
1243 MSG msg={0,};
1244 int vvv = GetMessage(&msg,NULL,0,0);
1245 if (!vvv) break;
1247 if (vvv<0)
1249 Sleep(10);
1250 continue;
1252 if (!msg.hwnd)
1254 DispatchMessage(&msg);
1255 continue;
1257 if (SWELLAppMain(SWELLAPP_PROCESSMESSAGE, (INT_PTR) &msg, 0)) continue;
1259 if (g_editor.m_hwnd && IsDialogMessage(g_editor.m_hwnd,&msg)) continue;
1261 TranslateMessage(&msg);
1262 DispatchMessage(&msg);
1265 SWELLAppMain(SWELLAPP_DESTROY,0,0);
1267 ExitProcess(0);
1269 return 0;
1272 #else
1274 /************** SWELL stuff ********** */
1276 #ifdef __APPLE__
1277 extern "C" {
1278 #endif
1280 const char **g_argv;
1281 int g_argc;
1283 #ifdef __APPLE__
1285 #endif
1288 #ifndef __APPLE__
1290 int main(int argc, const char **argv)
1292 g_argc=argc;
1293 g_argv=argv;
1294 SWELL_initargs(&argc,(char***)&argv);
1295 SWELL_Internal_PostMessage_Init();
1296 SWELL_ExtendedAPI("APPNAME",(void*)"LangPackEdit");
1297 SWELLAppMain(SWELLAPP_ONLOAD,0,0);
1298 SWELLAppMain(SWELLAPP_LOADED,0,0);
1299 while (!g_quit) {
1300 SWELL_RunMessageLoop();
1301 Sleep(10);
1303 SWELLAppMain(SWELLAPP_DESTROY,0,0);
1304 return 0;
1307 #endif
1310 #include "../../swell/swell-dlggen.h"
1311 #include "res.rc_mac_dlg"
1312 #undef BEGIN
1313 #undef END
1314 #include "../../swell/swell-menugen.h"
1315 #include "res.rc_mac_menu"
1317 #endif