convert line ends
[canaan.git] / prj / cam / src / editor / editlink.cpp
blob7742f22b76f43930662673114c16367130be8ee1
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/editor/editlink.cpp,v 1.14 1998/02/04 13:53:26 dc Exp $
7 // Object link editor
9 #include <stdlib.h>
11 #include <editlink.h>
12 #include <edlinkst.h>
14 #include <sdesc.h>
15 #include <isdesced.h>
16 #include <isdescst.h>
18 #include <link.h>
19 #include <linkbase.h>
20 #include <linkman.h>
21 #include <lnkquery.h>
22 #include <linkid.h>
24 #include <relation.h>
26 #include <objsys.h>
27 #include <objedit.h>
29 #include <mprintf.h>
31 // for the windows controls
32 #define WIN32_LEAN_AND_MEAN
33 #include <windows.h>
34 #include <commdlg.h>
35 #include <commctrl.h>
37 #include <comtools.h>
38 #include <wappapi.h>
39 #include <appagg.h>
41 #include <dialogs.h>
43 // must be last header
44 #include <dbmem.h>
47 // Link Editor
50 struct LinkEditor
52 LinkEditorDesc editdesc;
53 HWND hWnd;
54 HWND hMainWnd;
55 HINSTANCE hInst;
56 HWND hWndList;
57 ObjID objid_src;
58 ObjID objid_dest;
59 RelationID relid;
60 HFONT hWndFont;
63 // property IDs
64 #define ID_LINKED "LinkEditor: ed"
66 // control IDs
67 #define ID_ADDBUTTON 0
68 #define ID_DELETEBUTTON 1
69 #define ID_DONEBUTTON 2
70 #define ID_LISTVIEW 5
72 // END for the windows controls
74 #define MAX_STRLEN 32
76 // add a single line (LinkID, source objid, dest objid, flavor) to the list view
78 static LinkID get_item_linkid(LinkEditor* ed, int idx)
80 LV_ITEM item;
81 item.iItem = idx;
82 item.iSubItem = 0;
83 item.mask = LVIF_PARAM;
84 if (ListView_GetItem(ed->hWndList,&item))
85 return (LinkID)item.lParam;
86 else
87 return LINKID_NULL;
90 extern BOOL show_internal_links = FALSE;
92 BOOL LinkEditorShowAllLinks(BOOL newval)
94 BOOL old = show_internal_links;
95 show_internal_links = newval;
96 return old;
99 BOOL linkedit_fill_line(HWND hWnd, int index, LinkID id, sLinkAndData* link)
101 char buf[MAX_STRLEN];
102 AutoAppIPtr_(LinkManager,LinkMan);
103 IRelation* rel = LinkMan->GetRelation(link->flavor);
104 const char* flavor = rel->Describe()->name;
106 ListView_SetItemText(hWnd, index, 0, (char*)flavor);
107 sprintf(buf, "%08X", id);
108 ListView_SetItemText(hWnd, index, 1, buf);
110 ListView_SetItemText(hWnd, index, 2, (char*)ObjEditName(link->source));
111 ListView_SetItemText(hWnd, index, 3, (char*)ObjEditName(link->dest));
112 if (link->data != NULL)
114 AutoAppIPtr_(StructDescTools,pTools);
115 const sRelationDataDesc* ddesc = rel->DescribeData();
116 const sStructDesc* sdesc = pTools->Lookup(ddesc->type);
117 if (!sdesc)
118 sprintf(buf,"%08X",link->data);
119 else
121 pTools->UnparseSimple(sdesc,link->data,buf,sizeof(buf));
123 ListView_SetItemText(hWnd, index, 4, buf);
126 SafeRelease(rel);
127 return TRUE;
133 void LGAPI edit_cb(sStructEditEvent* , StructEditCBData data);
135 static void linkedit_edit_data(LinkEditor* ed, int idx)
137 LinkID id = get_item_linkid(ed,idx);
138 if (id == LINKID_NULL) return;
140 AutoAppIPtr_(LinkManager,LinkMan);
141 void* data = LinkMan->GetData(id);
145 IRelation* Rel = LinkMan->GetRelation(LINKID_RELATION(id));
146 const sRelationDataDesc* ddesc = Rel->DescribeData();
148 if (ddesc->size <= 0)
149 return;
151 if (data == NULL)
153 char* blank = new char[ddesc->size];
154 memset(blank,0,ddesc->size);
155 Rel->SetData(id,blank);
156 data = Rel->GetData(id);
157 delete blank;
160 AutoAppIPtr_(StructDescTools,pTools);
162 const sStructDesc* sdesc = pTools->Lookup(ddesc->type);
163 if (sdesc)
165 sStructEditorDesc editdesc = { "", kStructEditAllButtons};
166 strcpy(editdesc.title,ddesc->type);
167 IStructEditor* sed = CreateStructEditor(&editdesc,(sStructDesc*)sdesc,data);
168 sed->SetCallback(edit_cb,(StructEditCBData)ed);
169 sed->Go(kStructEdModeless);
170 SafeRelease(sed);
173 SafeRelease(Rel);
176 int linkedit_add_line(HWND hWnd, int index, LinkID id, sLinkAndData* link)
178 LV_ITEM item;
181 item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
182 item.iItem = index;
183 item.iSubItem = 0;
184 item.state = 0;
185 item.stateMask = 0;
186 item.pszText = "";
187 item.cchTextMax = MAX_STRLEN;
188 item.lParam = id;
189 ListView_InsertItem(hWnd, &item);
190 linkedit_fill_line(hWnd,index,id,link);
191 return index;
196 // start at item #start (0 means start at beginning of list)
197 int linkedit_find_selected(HWND hWnd, int start)
199 if (ListView_GetSelectedCount(hWnd))
200 for (int i=start; i<ListView_GetItemCount(hWnd); i++)
201 if (ListView_GetItemState(hWnd, i, LVIS_SELECTED))
202 return i;
204 return -1;
207 // this is to be filled in by the AddLinkDlgProc
208 // this is static because there can be only one link add dialog at a time, and we
209 // can't attach a LinkEditor* to the dialog
210 static sLinkAndData dlg_add_link;
212 #pragma off(unreferenced)
213 // add link dialog DlgProc
214 BOOL CALLBACK AddLinkDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
216 switch (msg)
218 case WM_INITDIALOG:
219 ILinkManager* pLinkMan;
220 const sRelationDesc* reldesc;
221 sRelationIter iter;
222 RelationID flavor;
224 flavor = 0;
225 pLinkMan = AppGetObj(ILinkManager);
227 // go through the list of all flavors and add them to the dropdown box
228 pLinkMan->IterStart(&iter);
229 while (pLinkMan->IterNext(&iter, &flavor))
231 IRelation* pRel = pLinkMan->GetRelation(flavor);
232 if (pRel)
233 reldesc = pRel->Describe();
234 if (show_internal_links || !(reldesc->flags & kRelationNoEdit))
236 int idx = SendDlgItemMessage(hDlg, IDC_FLAVORCOMBO, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)reldesc->name);
237 SendDlgItemMessage(hDlg, IDC_FLAVORCOMBO, CB_SETITEMDATA, (WPARAM)idx, (LPARAM)pRel->GetID());
240 SafeRelease(pRel);
242 pLinkMan->IterStop(&iter);
243 SafeRelease(pLinkMan);
244 return TRUE;
246 case WM_COMMAND:
247 switch (LOWORD(wParam))
249 case IDOK:
251 // add the new link from the dialog
252 char buf[16];
254 GetDlgItemText(hDlg, IDC_FROMEDIT, buf, 15);
255 dlg_add_link.source = EditGetObjNamed(buf);
257 GetDlgItemText(hDlg, IDC_TOEDIT, buf, 15);
258 dlg_add_link.dest = EditGetObjNamed(buf);
260 dlg_add_link.data = NULL;
261 int idx = SendDlgItemMessage(hDlg, IDC_FLAVORCOMBO, CB_GETCURSEL, 0, 0);
262 int id = SendDlgItemMessage(hDlg, IDC_FLAVORCOMBO, CB_GETITEMDATA, (WPARAM)idx, 0);
264 dlg_add_link.flavor = (RelationID)id;
265 EndDialog(hDlg, TRUE);
267 return TRUE;
269 case IDCANCEL:
270 EndDialog(hDlg, FALSE);
271 return FALSE;
273 case IDC_DATAEDIT:
274 MessageBox(hDlg, "edit data here", "Info", MB_OK | MB_ICONINFORMATION);
275 break;
277 break;
280 return FALSE;
282 #pragma on(unreferenced)
284 // puts up the dialog to add a link and does it
285 void linkedit_add_link(LinkEditor* ed, HWND hWnd)
287 sLinkAndData& link = dlg_add_link;
289 memset(&link, 0, sizeof(sLinkAndData));
291 if (DialogBox(ed->hInst, "AddLink", hWnd, AddLinkDlgProc))
293 AutoAppIPtr_(LinkManager,pLinkMan);
294 const sRelationDesc* reldesc = NULL;
296 if (link.flavor == 0)
298 MessageBeep(MB_ICONEXCLAMATION);
299 MessageBox(hWnd, "Flavor is invalid!", "Add Link Error", MB_OK | MB_ICONEXCLAMATION);
300 return;
303 // get the text description of the flavor
304 pLinkMan = AppGetObj(ILinkManager);
305 cAutoIPtr<IRelation> pRel (pLinkMan->GetRelation(link.flavor));
306 if (pRel)
307 reldesc = pRel->Describe();
309 // can't link myself to myself
310 if (link.source == link.dest)
312 MessageBeep(MB_ICONEXCLAMATION);
313 MessageBox(hWnd, "Source and dest ObjID's can't be the same", "Add Link Error", MB_OK | MB_ICONEXCLAMATION);
314 return;
316 // check for valid objids
317 if (!ObjectExists(link.source))
319 char str[80];
321 sprintf(str, "Source ObjID %d is invalid!\n", link.source);
322 MessageBeep(MB_ICONEXCLAMATION);
323 MessageBox(hWnd, str, "Add Link Error", MB_OK | MB_ICONEXCLAMATION);
324 return;
326 if (!ObjectExists(link.dest))
328 char str[80];
330 sprintf(str, "Dest ObjID %d is invalid!\n", link.dest);
331 MessageBeep(MB_ICONEXCLAMATION);
332 MessageBox(hWnd, str, "Add Link Error", MB_OK | MB_ICONEXCLAMATION);
333 return;
336 LinkID id = pLinkMan->Add(link.source,link.dest,link.flavor);
337 link.data = pLinkMan->GetData(id);
338 int idx = linkedit_add_line(hWnd, ListView_GetItemCount(hWnd), id, &link);
339 if (link.data != NULL)
340 linkedit_edit_data(ed,idx);
344 // called when the delete button is pressed and a link is selected
345 void linkedit_delete_link(LinkEditor* ed, HWND hWnd, int index)
347 LinkID id = get_item_linkid(ed,index);
348 if (id == LINKID_NULL) return;
349 AutoAppIPtr_(LinkManager,LinkMan);
350 LinkMan->Remove(id);
351 ListView_DeleteItem(hWnd, index);
354 // finds all selected links and deletes them all
355 void linkedit_delete_selected(LinkEditor* ed, HWND hWnd)
357 int num = linkedit_find_selected(hWnd, -1);
358 if (num != -1)
360 MessageBeep(MB_ICONHAND);
361 int result = MessageBox(hWnd, "Are you sure?", "Delete Link", MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
362 if (result == IDYES)
366 linkedit_delete_link(ed, hWnd, num);
368 while ((num = linkedit_find_selected(hWnd, num)) != -1);
373 // add all links for one objid to the list box
374 void linkedit_add_all_items(HWND hWnd, ObjID objid_src, ObjID objid_dest, RelationID relid)
376 ILinkManager* pLinkMan = AppGetObj(ILinkManager);
377 ILinkQuery* pQuery;
378 int index = 0;
380 // add the items which match the given query
381 pQuery = pLinkMan->Query(objid_src, objid_dest, relid);
382 for (; !pQuery->Done(); pQuery->Next())
384 sLinkAndData link;
385 memset(&link,0,sizeof(link));
387 LinkID id = pQuery->ID();
388 IRelation* rel = pLinkMan->GetRelation(LINKID_RELATION(id));
389 const sRelationDesc* desc = rel->Describe();
390 if (!show_internal_links && (desc->flags & kRelationNoEdit))
392 SafeRelease(rel);
393 continue;
395 SafeRelease(rel);
396 pQuery->Link(&link);
397 link.data = pQuery->Data();
398 linkedit_add_line(hWnd, index++, id, &link);
400 SafeRelease(pQuery);
401 SafeRelease(pLinkMan);
404 // WndProc for the link editor
405 LRESULT CALLBACK LinkEditorWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
407 HWND parent;
408 LinkEditor *ed;
410 // get my parent so I can get the LinkEditor* from it
411 parent = GetParent(hWnd);
412 if (parent == NULL)
413 parent = hWnd;
414 ed = (LinkEditor*) GetProp(parent, ID_LINKED);
416 switch(msg)
418 case WM_COMMAND:
419 switch(LOWORD(wParam))
421 case ID_ADDBUTTON:
422 linkedit_add_link(ed, ed->hWndList);
423 break;
425 case ID_DELETEBUTTON:
426 linkedit_delete_selected(ed, ed->hWndList);
427 break;
429 case ID_DONEBUTTON:
430 DestroyWindow(ed->hWnd);
431 break;
434 // keep the list view active so we can see what's highlighted
435 SetFocus(ed->hWndList);
436 return 0;
438 case WM_NOTIFY:
439 NMHDR *hdr;
440 hdr = (NMHDR*)lParam;
441 if (hdr->code == NM_DBLCLK)
443 int idx = linkedit_find_selected(ed->hWndList, 0);
444 if (idx != -1)
446 linkedit_edit_data(ed,idx);
449 break;
451 case WM_DESTROY:
452 LinkEditorDestroy(ed);
453 return 0;
456 return DefWindowProc(hWnd, msg, wParam, lParam);
459 static const char* relid_name(RelationID id)
461 if (id == RELID_NULL) return "any";
463 ILinkManager* linkman = AppGetObj(ILinkManager);
464 IRelation* rel = linkman->GetRelation(id);
465 const char* out = rel->Describe()->name;
466 SafeRelease(linkman);
467 SafeRelease(rel);
468 return out;
471 #define objid_name(obj) ((obj == LINKOBJ_WILDCARD) ? "any" : ObjEditName(obj))
473 static const char* default_title(ObjID src, ObjID dst, RelationID relid)
475 static char buf[96];
477 sprintf(buf,"Links: %s --[%s]--> %s",objid_name(src),relid_name(relid),objid_name(dst));
479 return buf;
482 // create me a link editor for an objid
483 LinkEditor* LinkEditorCreate(ObjID objid_src, ObjID objid_dest, RelationID relid, LinkEditorDesc* editdesc)
485 LinkEditor* ed;
486 const char* szName = "Link Editor";
487 WNDCLASS wc;
488 HWND hMainWnd;
489 HINSTANCE hMainInst;
490 IWinApp* pWA;
491 LV_COLUMN col;
492 int width, height, left, top;
494 ed = (LinkEditor*)Malloc(sizeof(LinkEditor));
496 // we need the main app's HWND
497 pWA = AppGetObj(IWinApp);
498 hMainWnd = pWA->GetMainWnd();
499 SafeRelease(pWA);
501 // we also need the main app's HINSTANCE
502 hMainInst = (HINSTANCE) GetWindowLong(hMainWnd, GWL_HINSTANCE);
504 // set up the new window's class
505 memset(&wc, 0, sizeof(WNDCLASS));
506 wc.style = CS_HREDRAW | CS_VREDRAW;
507 wc.lpfnWndProc = LinkEditorWndProc;
508 wc.hInstance = hMainInst;
509 wc.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
510 wc.lpszClassName = szName;
512 // and register it
513 RegisterClass(&wc);
515 // make it so big
516 width = 500;
517 height = 400;
518 left = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
519 top = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;
522 const char* title = editdesc->title;
523 if (*title == '\0')
524 title = default_title(objid_src,objid_dest,relid);
526 // and create it!
527 ed->hWnd = CreateWindow(szName, (char*)title, WS_OVERLAPPED, left, top, width, height,
528 hMainWnd, NULL, hMainInst, NULL);
530 ShowWindow(ed->hWnd, SW_SHOWNORMAL);
531 UpdateWindow(ed->hWnd);
533 // Create a nice standard Windows font for the controls.
534 LOGFONT logFont;
535 memset(&logFont, 0, sizeof(LOGFONT));
536 logFont.lfCharSet = DEFAULT_CHARSET;
537 logFont.lfHeight = 12;
538 strcpy(logFont.lfFaceName, "MS Sans Serif");
539 ed->hWndFont = CreateFontIndirect(&logFont);
541 // get our new window's HINSTANCE
542 ed->hInst = (HINSTANCE) GetWindowLong(ed->hWnd, GWL_HINSTANCE);
544 SetProp(ed->hWnd, ID_LINKED, (HANDLE)ed);
545 ed->hMainWnd = hMainWnd;
546 ed->objid_src = objid_src;
547 ed->objid_dest = objid_dest;
548 ed->relid = relid;
550 // create some buttons
551 HWND btnWnd;
552 btnWnd = CreateWindow("BUTTON", "Add", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
553 60, 340, 65, 25, ed->hWnd, (HMENU) ID_ADDBUTTON, ed->hInst, NULL);
554 if (ed->hWndFont)
555 SendMessage(btnWnd, WM_SETFONT, (WPARAM)ed->hWndFont, 0);
557 btnWnd = CreateWindow("BUTTON", "Delete", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
558 135, 340, 65, 25, ed->hWnd, (HMENU) ID_DELETEBUTTON, ed->hInst, NULL);
559 if (ed->hWndFont)
560 SendMessage(btnWnd, WM_SETFONT, (WPARAM)ed->hWndFont, 0);
562 btnWnd = CreateWindow("BUTTON", "Done", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
563 375, 340, 65, 25, ed->hWnd, (HMENU) ID_DONEBUTTON, ed->hInst, NULL);
564 if (ed->hWndFont)
565 SendMessage(btnWnd, WM_SETFONT, (WPARAM)ed->hWndFont, 0);
568 if (ed->hWndFont)
569 SendMessage(btnWnd, WM_SETFONT, (WPARAM)ed->hWndFont, 0);
571 // make it fill most of the window
572 width = 500 - GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
573 height = 330;
574 left = 0;
575 top = 0;
577 // now, let's create the list view
578 ed->hWndList = CreateWindowEx(0L, WC_LISTVIEW, "", WS_VISIBLE | WS_CHILD | LVS_REPORT, top,
579 left, width, height, ed->hWnd, (HMENU) ID_LISTVIEW, ed->hInst, NULL);
581 // set up the columns
582 col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
583 col.fmt = LVCFMT_LEFT;
584 col.cx = 125;
585 col.pszText = "Flavor";
586 ListView_InsertColumn(ed->hWndList, 0, &col);
587 col.cx = 75;
588 col.pszText = "ID";
589 ListView_InsertColumn(ed->hWndList, 1, &col);
590 col.cx = 100;
591 col.pszText = "From";
592 ListView_InsertColumn(ed->hWndList, 2, &col);
593 col.pszText = "To";
594 ListView_InsertColumn(ed->hWndList, 3, &col);
595 col.cx = 75;
596 col.fmt = LVCFMT_RIGHT;
597 col.pszText = "Data";
598 ListView_InsertColumn(ed->hWndList, 4, &col);
600 // insert all of the items
601 linkedit_add_all_items(ed->hWndList, ed->objid_src, ed->objid_dest, ed->relid);
603 return ed;
606 // destroy the link editor
607 void LinkEditorDestroy(LinkEditor* ed)
609 if (ed->hWndFont)
611 DeleteObject(ed->hWndFont);
612 ed->hWndFont = 0;
614 SetFocus(ed->hMainWnd);
615 RemoveProp(ed->hWnd, ID_LINKED);
616 Free(ed);
619 static void refresh_all_fields(LinkEditor* ed)
621 AutoAppIPtr_(LinkManager,LinkMan);
622 int max = ListView_GetItemCount(ed->hWndList);
623 for (int idx = 0; idx < max; idx++)
625 LinkID id = get_item_linkid(ed,idx);
626 if (id == LINKID_NULL)
627 continue;
628 sLinkAndData link;
629 LinkMan->Get(id,&link);
630 link.data = LinkMan->GetData(id);
631 linkedit_fill_line(ed->hWndList,idx,id,&link);
635 static void LGAPI edit_cb(sStructEditEvent* , StructEditCBData data)
637 LinkEditor* ed = (LinkEditor*)data;
638 refresh_all_fields(ed);