+// MainWnd.cpp: implementation of the CMainWnd class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "mainwnd.h"
+#include "memfile.h"
+#include "md3.h"
+#include "wild.h"
+#include "renamedlg.h"
+#ifdef __linux__
+#include <dirent.h>
+#include <stdlib.h>
+#endif
+
+extern CMainWnd *g_pMainWnd;
+bool g_bIgnoreCommands=FALSE;
+
+GtkWidget* create_menu_in_menu (GtkWidget *menu, gchar *label, GtkAccelGroup *menu_accel,GtkAccelGroup **submenu_accel);
+GtkWidget* create_menu_item (GtkWidget *menu, gchar *label, GtkAccelGroup *menu_accel,GtkSignalFunc func, int id);
+GtkWidget* menu_separator (GtkWidget *menu);
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+static gint mainwnd_delete (GtkWidget *widget, gpointer data)
+{
+return g_pMainWnd->OnClose();
+}
+
+static void HandleCommand (GtkWidget *widget, gpointer data)
+{
+ int id = GPOINTER_TO_INT (data);
+
+ if (g_bIgnoreCommands)
+ return;
+
+ switch (id)
+ {
+ case ID_FILE_NEW: g_pMainWnd->OnFileNew (); break;
+ case ID_FILE_OPEN: g_pMainWnd->OnFileOpen (); break;
+ case ID_FILE_SAVE: g_pMainWnd->OnFileSave (); break;
+
+ case ID_WIZARD: g_pMainWnd->OnWizard (); break;
+
+ case ID_VIEW: g_pMainWnd->OnView (); break;
+ case ID_EXTRACT: g_pMainWnd->OnExtract (); break;
+
+ case ID_ADD: g_pMainWnd->OnAdd (); break;
+ case ID_RENAME: g_pMainWnd->OnRename (); break;
+ case ID_DELETE: g_pMainWnd->OnDelete (); break;
+ }
+}
+
+CMainWnd::CMainWnd()
+{
+ m_pMainWnd=NULL;
+ m_mnuDrop=NULL;
+}
+
+CMainWnd::~CMainWnd()
+{
+ if (m_pMainWnd)
+ delete m_pMainWnd;
+}
+
+bool CMainWnd::OnClose()
+{
+ if (pak.IsModified())
+ {
+ //int ret=DoMessageBox("Pk3 has not been saved\n\n Really quit?","Pk3Man",MB_YESNO);
+ int ret=g_FuncTable.m_pfnMessageBox(m_pMainWnd,"Pk3 has not been saved\n\n Really quit?","Pk3Man",MB_YESNO, NULL);
+ if (ret!=IDYES)
+ return 1;
+ }
+
+ FreePakList();
+ DeleteTempFiles();
+ return 0;
+}
+
+void CMainWnd::OnDestroy()
+{
+ // NOT CALLED !!!
+}
+
+BOOL CMainWnd::Create(GtkWidget *parent)
+{
+ GtkWidget *vbox,*window;
+ GdkPixmap *pixmap=NULL;
+ GdkBitmap *mask=NULL;
+
+ char * icon_xpm[] = { "32 32 6 1", ". c none", "/ c #ff0ff", "0 c #dddddd", "1 c #e6a0e6", "2 c #333333", "3 c #9d189d", "................................", "................................", "............////////............", "............////////............", "........////////////////........", "........////////////////........", "......////////////////////......", "......////////////////////......", "....//////0000////////0001//....", "....//////0000////////0000//....", "....////00000000////00000001....", "....////00000000////00000001....", "....////00002222////00002223....", "....////00002222////00002223....", "..//////00002222////00002223//..", "..//////00002222////00002222//..", "..////////0000////////0000////..", "..////////0000////////0000////..", "..////////////////////////////..", "..////////////////////////////..", "..////////////////////////////..", "..////////////////////////////..", "..////////////////////////////..", "..////////////////////////////..", "..////////////////////////////..", "..////////////////////////////..", "..////..//////....//////..////..", "..////..//////....//////..////..", "..//......////....////......//..", "..//......////....////......//..", "................................", "................................"};
+
+ // get bitmap paths
+ GetPaths();
+
+ // create the main widget
+ m_pMainWnd = window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (mainwnd_delete), this);
+ //gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroy), this);
+
+ gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER);
+ gtk_window_set_title (GTK_WINDOW (m_pMainWnd), "Pk3Man - ");
+ gtk_window_set_default_size (GTK_WINDOW (m_pMainWnd), 300, 300);
+ gtk_widget_show (m_pMainWnd);
+
+ // load and set the icon
+
+ pixmap = gdk_pixmap_create_from_xpm_d (window->window, &mask, NULL, icon_xpm);
+ gdk_window_set_icon (window->window, NULL, pixmap, mask);
+
+ // setup the vertical layout box
+ vbox=gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_widget_show (vbox);
+
+ if (!CreateToolbar (window, vbox))
+ return FALSE;
+
+ if (!CreateTreeView (window, vbox))
+ return FALSE;
+
+ if (!CreateStatusBar (window,vbox))
+ return FALSE;
+
+ UpdateStatus();
+ UpdateToolBar();
+
+ LoadExclusions();
+ InitPakList();
+
+ return TRUE;
+}
+
+BOOL CMainWnd::CreateTreeView(GtkWidget *window,GtkWidget *vbox)
+{
+ // create a scrollable widget to house the tree
+ m_ScrolledWin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_ScrolledWin),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ //gtk_widget_set_usize (m_ScrolledWin, 400, 400);
+ gtk_box_pack_start (GTK_BOX (vbox), m_ScrolledWin, TRUE, TRUE, 0);
+ gtk_widget_show (m_ScrolledWin);
+
+ // attach and initialise the tree
+ m_Tree.Init(m_ScrolledWin);
+
+ return TRUE;
+}
+
+BOOL CMainWnd::CreateToolbar(GtkWidget *window,GtkWidget *vbox)
+{
+ GtkWidget *handle_box, *toolbar, *w;
+
+ handle_box = gtk_handle_box_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), handle_box, FALSE, FALSE, 0);
+ gtk_widget_show (handle_box);
+
+ m_Toolbar = toolbar = gtk_toolbar_new ();
+
+ // gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), user_rc.toolbar_style);
+ gtk_container_add (GTK_CONTAINER (handle_box), toolbar);
+ gtk_container_border_width (GTK_CONTAINER (toolbar), 2);
+ gtk_widget_show (toolbar);
+
+ // new
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "New",
+ "Start a new Pk3",
+ "",
+ CPixMap::new_pixmap(window, "pk3man_tbnew.bmp"),
+ //CPixMap::pixmap_from_char(window,xpm_new),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_FILE_NEW));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_file_new", w);
+
+ // open
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "Open",
+ "Open an existing Pk3",
+ "",
+ CPixMap::new_pixmap(window, "pk3man_tbopen.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_FILE_OPEN));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_file_open", w);
+
+ // save
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "Save",
+ "Save the current Pk3",
+ "",
+ CPixMap::new_pixmap (window, "pk3man_tbsave.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_FILE_SAVE));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_file_save", w);
+
+ // space
+ gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
+
+ // wizard
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "Wizard",
+ "Build Pk3 from a Map",
+ "",
+ CPixMap::new_pixmap (window, "pk3man_tbwizard.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_WIZARD));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_wizard", w);
+
+ // space
+ gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
+
+ // view
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "View",
+ "View an entry",
+ "",
+ CPixMap::new_pixmap (window, "pk3man_tbview.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_VIEW));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_view", w);
+
+ // extract
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "Extract",
+ "Extract an entry",
+ "",
+ CPixMap::new_pixmap (window, "pk3man_tbextract.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_EXTRACT));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_extract", w);
+
+ // space
+ gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
+
+ // Add
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "Wizard",
+ "Add a file",
+ "",
+ CPixMap::new_pixmap (window, "pk3man_tbadd.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_ADD));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_add", w);
+
+ // rename
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "Rename",
+ "Rename an entry",
+ "",
+ CPixMap::new_pixmap (window, "pk3man_tbrename.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_RENAME));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_rename", w);
+
+
+ // rename
+ w = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
+ "Delete",
+ "Delete an entry",
+ "",
+ CPixMap::new_pixmap (window, "pk3man_tbdelete.bmp"),
+ GTK_SIGNAL_FUNC (HandleCommand),
+ GINT_TO_POINTER (ID_DELETE));
+
+ gtk_object_set_data (GTK_OBJECT (window), "tb_delete", w);
+
+ return TRUE;
+}
+
+void CMainWnd::UpdateToolBar()
+{
+ std::string path=m_Tree.GetSelected();
+ bool file_selected=TRUE;
+
+ if (path=="")
+ file_selected=FALSE;
+
+ if (file_selected)
+ {
+ // enable toolbar buttons
+
+ GtkWidget *item;
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_view"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), FALSE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_extract"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), FALSE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_delete"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), FALSE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_rename"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), FALSE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ }
+ else
+ {
+ // disable toolbar buttons
+
+ GtkWidget *item;
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_view"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), TRUE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_extract"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), TRUE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_delete"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), TRUE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ item = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (m_pMainWnd), "tb_rename"));
+ //gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), TRUE);
+ gtk_widget_set_sensitive (item, TRUE);
+
+ }
+
+}
+
+BOOL CMainWnd::CreateStatusBar(GtkWidget *window,GtkWidget *vbox)
+{
+ GtkWidget *hbox, *hbox1;
+ GtkWidget *frame;
+ GtkWidget *label;
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (hbox);
+ gtk_widget_set_usize (hbox, -1, 24);
+ gtk_container_border_width (GTK_CONTAINER (hbox), 1);
+ gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, TRUE, 2);
+
+ frame = gtk_frame_new ((char*)NULL);
+ gtk_widget_show (frame);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+
+ hbox1 = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), hbox1);
+ gtk_container_border_width (GTK_CONTAINER (hbox1), 0);
+ gtk_widget_show (hbox1);
+
+ label = gtk_label_new (" Ready ... ");
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, TRUE, 0);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_misc_set_padding (GTK_MISC (label), 3, 0);
+ m_pStatusLabel[0] = label;
+
+ for (int i = 1; i < 3; i++)
+ {
+ frame = gtk_frame_new ((char*)NULL);
+ gtk_widget_show (frame);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+
+ label = gtk_label_new (" ");
+ gtk_widget_show (label);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+ m_pStatusLabel[i] = label;
+ }
+
+ return TRUE;
+}
+
+void CMainWnd::OnFileOpen()
+{
+ // lets pick a file
+ const char *filename;
+
+ if (g_strModPath.GetLength()>0)
+ //filename=file_dialog(TRUE,"Choose a Pk3...",g_strModPath.GetBuffer(),"*.pk3");
+ filename=g_FuncTable.m_pfnFileDialog (m_pMainWnd, TRUE, "Choose a Pk3...",
+ g_strModPath.GetBuffer(), "pk3man");
+ else
+ //filename=file_dialog(TRUE,"Choose a Pk3...",g_strBasePath.GetBuffer(),"*.pk3");
+ filename=g_FuncTable.m_pfnFileDialog (m_pMainWnd, TRUE, "Choose a Pk3...",
+ g_strBasePath.GetBuffer(), "pk3man");
+
+ if (filename==NULL)
+ return;
+
+ Str fname=filename;
+ fname=fname.Right(3);
+ if (fname!="pk3")
+ {
+ //DoMessageBox("Please pick a Pk3 file","Pk3Man",MB_OK);
+ g_FuncTable.m_pfnMessageBox(m_pMainWnd,"Please pick a Pk3 file","Pk3Man",MB_OK, NULL);
+ return;
+ }
+
+ OnFileNew();
+ pak.Open(filename);
+ UpdateTree();
+
+ // update window title
+ g_FuncTable.m_pfnQE_ConvertDOSToUnixName (filename, filename);
+ std::string title=filename;
+ g_free (filename);
+ long pos=title.find_last_of('/');
+ title=title.substr(pos+1);
+ title="Pk3Man - "+title;
+ gtk_window_set_title (GTK_WINDOW (m_pMainWnd), title.c_str());
+}
+
+void CMainWnd::OnFileSave()
+{
+ const char *filename;
+
+ if (g_strModPath.GetLength()>0)
+ filename=g_FuncTable.m_pfnFileDialog (m_pMainWnd, FALSE, "Save As...",
+ g_strModPath.GetBuffer(), "pk3man");
+ else
+ filename=g_FuncTable.m_pfnFileDialog (m_pMainWnd, FALSE, "Save As...",
+ g_strBasePath.GetBuffer(), "pk3man");
+
+ if (filename==NULL)
+ return;
+
+ g_FuncTable.m_pfnQE_ConvertDOSToUnixName (filename, filename);
+ std::string path=filename;
+ g_free (filename);
+ long p=path.find_last_of('/');
+ std::string name=path.substr(p,-1);
+
+ std::string tpath=g_strTempPath.GetBuffer() + name;
+
+ if (!pak.Save(tpath.c_str()))
+ {
+ // eek, error central
+ g_FuncTable.m_pfnMessageBox(m_pMainWnd,"Unable to save the pk3 to temp folder!","Pk3Man",MB_OK, NULL);
+ return;
+ }
+
+ // ok, now copy to proper place
+
+#ifdef WIN32
+
+ int ret=CopyFile(tpath.c_str(),path.c_str(),FALSE);
+ DeleteFile(tpath.c_str());
+
+ if (!ret)
+ {
+ g_FuncTable.m_pfnMessageBox(m_pMainWnd,"Unable to copy pk3 from temp folder!","Pk3Man",MB_OK, NULL);
+ return;
+ }
+
+#endif
+
+ pak.Close();
+ pak.Open(path.c_str());
+ m_Tree.Clear();
+ UpdateTree();
+
+ //update title
+ name=name.substr(1,-1);
+ std::string title="Pk3Man - "+name;
+ gtk_window_set_title (GTK_WINDOW (m_pMainWnd), title.c_str());
+}
+
+bool CMainWnd::OnFileNew()
+{
+ if (pak.IsModified())
+ {
+ int ret=g_FuncTable.m_pfnMessageBox(m_pMainWnd,"This will lose the current Pk3 contents\n\n Are u sure?","Pk3Man",MB_YESNO, NULL);
+ if (ret!=IDYES)
+ return FALSE;
+ }
+
+ pak.Close();
+ m_Tree.Clear();
+ gtk_window_set_title (GTK_WINDOW (m_pMainWnd), "Pk3Man - ");
+
+ return TRUE;
+}
+
+void CMainWnd::LoadExclusions()
+{
+ exclusions.clear();
+
+ FILE *fp;
+ std::string filename=g_strAppPath.GetBuffer();
+ filename+="plugins/pk3man.exclude";
+
+ fp=fopen(filename.c_str(),"rb");
+ if (!fp)
+ return;
+
+ while (!feof(fp))
+ {
+ std::string line=GetLine(fp);
+
+ if (line.length()==0)
+ continue;
+
+ if (line[0]=='/' && line[1]=='/')
+ continue;
+
+ for (int n=0 ; n < (int)line.length() ; n++)
+ {
+ if (line[n]=='\\')
+ line[n]='/';
+ }
+
+ if (line[0]=='/')
+ line=line.substr(1,-1);
+
+ exclusions.push_back(line);
+ }
+}
+
+bool CMainWnd::IsExcluded(const char *name)
+{
+ for (std::list<std::string>::iterator i=exclusions.begin() ; i!=exclusions.end() ; i++)
+ {
+ std::string t=*i;
+
+ if (CWild::fpattern_match(t.c_str(),name))
+ return TRUE;
+
+ std::string nm=name;
+ if (nm.find(t) != static_cast<size_t>(-1))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void CMainWnd::OnWizard()
+{
+ if (!pak.IsEmpty())
+ {
+ int ret=g_FuncTable.m_pfnMessageBox(m_pMainWnd,"Add to current pak contents ?","Pk3Man",MB_YESNO, NULL);
+ if (ret==IDNO)
+ {
+ if (!OnFileNew())
+ return;
+ }
+
+ }
+
+ std::string mappath="maps";
+ mappath=g_strBasePath.GetBuffer()+mappath;
+
+ gchar *filename=g_FuncTable.m_pfnFileDialog(m_pMainWnd,TRUE,"Choose a Map...",mappath.c_str(),"pk3man");
+
+ if (filename==NULL)
+ return;
+
+ g_FuncTable.m_pfnQE_ConvertDOSToUnixName (filename, filename);
+ std::string map=filename;
+ long p=map.find_last_of('/');
+ map=map.substr(p+1,-1);
+
+ //update title
+ std::string title="Pk3Man - "+map;
+ gtk_window_set_title (GTK_WINDOW (m_pMainWnd), title.c_str());
+
+ // add map and aas
+ ProcessFile(map.c_str(),".bsp","maps");
+ ProcessFile(map.c_str(),".aas","maps");
+
+ // add levelshots
+ if (!ProcessFile(map.c_str(),".jpg","levelshots"))
+ ProcessFile(map.c_str(),".tga","levelshots");
+
+ // arena file
+ ProcessFile(map.c_str(),".arena","scripts");
+
+ // initialise text list
+ texlist.clear();
+
+ // generate item list
+ FILE *fp;
+ fp=fopen(filename,"r");
+ g_free(filename);
+
+ if (!fp)
+ return;
+
+ while (!feof(fp))
+ {
+ std::string line=GetLine(fp);
+
+ if (line.find("// brush")!=static_cast<size_t>(-1))
+ {
+ // its a brush
+ line=GetLine(fp);
+ line=GetLine(fp);
+
+ if (line.find("patchDef")!=static_cast<size_t>(-1))
+ {
+ // its a patch
+ line=GetLine(fp);
+ line=GetLine(fp);
+
+ // need to trim left and right
+ int n=line.find_first_not_of(' ');
+ if (n!=static_cast<size_t>(-1))
+ line=line.substr(n);
+
+ std::string tex="textures/"+line;
+ AddToTexList(line.c_str());
+ }
+ else
+ {
+ // its a standard brush
+ while (line.find_first_of('}')==static_cast<size_t>(-1))
+ {
+ long p=line.find_last_of(')');
+ line=line.substr(p+2,-1);
+
+ p=line.find_first_of(' ');
+ line=line.substr(0,p);
+
+ std::string tex="textures/"+line;
+ AddToTexList(line.c_str());
+
+ // next
+ line=GetLine(fp);
+ }
+ }
+ }
+ else if (line.find(".md3")!=static_cast<size_t>(-1))
+ {
+ long p=line.find_first_of(' ');
+ std::string tex=line.substr(p+2,-1);
+ tex=tex.substr(0,tex.length()-2);
+
+ AddToTexList(tex.c_str());
+ }
+ else if (line.find(".wav")!=static_cast<size_t>(-1))
+ {
+ long p=line.find_first_of(' ');
+ std::string tex=line.substr(p+2,-1);
+ tex=tex.substr(0,tex.length()-2);
+
+ AddToTexList(tex.c_str());
+ }
+
+ }
+ fclose(fp);
+
+ // ok, now proccess our texlist
+ // if we find a shader we add its textures onto the end of the texlist
+
+ for ( std::list<std::string>::const_iterator i = texlist.begin() ; i != texlist.end() ; i++)
+ {
+ std::string tex=*i;
+
+ if (tex=="")
+ continue;
+
+ std::string suffix=tex.substr(tex.length()-3,-1);
+
+ // is it a model?
+ if (suffix.compare("md3")==0)
+ {
+ ParseModel(tex.c_str());
+
+ // dont add md3's
+ continue;
+ }
+
+ // does it exist as it is
+ if (ProcessFile(tex.c_str()))
+ continue;
+
+ //shader
+ std::string shadername=tex;
+ const char *sname=NULL;
+
+ IShader *shader=g_ShadersTable.m_pfnShader_ForName_NoLoad(shadername.c_str());
+
+ if (shader)
+ sname=shader->getShaderFileName();
+
+ if (strlen(sname)>0)
+ {
+ ParseShader(sname,tex.c_str());
+
+ if (ProcessFile(sname))
+ continue;
+ }
+
+ // ok we got this far, its a texture
+
+ if (tex.find(".tga")!=static_cast<size_t>(-1) || tex.find(".jpg")!=static_cast<size_t>(-1))
+ tex=tex.substr(0,tex.length()-4);
+
+ // jpegs
+ std::string jpeg=tex;
+ if (jpeg.find("textures/")!=0)
+ jpeg="textures/"+jpeg;
+
+ jpeg+=".jpg";
+ if (ProcessFile(jpeg.c_str()))
+ continue;
+
+ // tga's
+ std::string tga=tex;
+ if (tga.find("textures/")!=0)
+ tga="textures/"+tga;
+
+ tga+=".tga";
+ if (ProcessFile(tga.c_str()))
+ continue;
+
+ }
+
+}
+
+void CMainWnd::ParseModel(const char *pathname)
+{
+ char* modelbuff;
+ int len = g_FuncTable.m_pfnLoadFile(pathname, (void**)&modelbuff);
+
+ if (len==0)
+ return;
+
+ CMemFile file(modelbuff,len);
+
+ md3_header_t header;
+
+ file.Read(&header,sizeof(header));
+
+ int n, numtags=header.Tag_num*header.BoneFrame_num;
+
+ for (n=0 ; n<numtags ; n++)
+ {
+ md3_tag_t tag;
+ file.Read(&tag,sizeof(md3_tag_t));
+ }
+
+ for (n=0 ; n<header.BoneFrame_num ; n++)
+ {
+ md3_boneframe_t bone;
+ file.Read(&bone,sizeof(md3_boneframe_t));
+ }
+
+ for (int m=0 ; m<header.Mesh_num ; m++)
+ {
+ md3_mesh_t mesh;
+ file.Read(&mesh,sizeof(md3_mesh_t));
+
+ // skins
+ for (n=0 ; n<mesh.Skin_num ; n++)
+ {
+ char SkinName[68];
+ file.Read(&SkinName,sizeof(char)*68);
+
+ // deal with the skin here
+ // we add it to the end of the list (if it aint already there)
+ // so we catch it later on
+
+ AddToTexList(SkinName);
+ }
+
+ // triangles
+ for (n=0 ; n<mesh.Triangle_num ; n++)
+ {
+ int Triangle[3];
+ file.Read(&Triangle,sizeof(int)*3);
+ }
+
+ // uv's
+ for (n=0 ; n<mesh.Vertex_num ; n++)
+ {
+ float uv[2];
+ file.Read(&uv,sizeof(float)*2);
+ }
+
+ // vertices
+ int numverts=mesh.Vertex_num*mesh.MeshFrame_num;
+
+ for (n=0 ; n<numverts ; n++)
+ {
+ md3_vertices_t verts;
+ file.Read(&verts,sizeof(md3_vertices_t));
+ }
+
+ }
+
+// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=491
+g_free(modelbuff);
+}
+
+void CMainWnd::AddToTexList(const char *buf)
+{
+ std::string tex;
+ int c=0;
+
+ while (buf[c]!=' ' && buf[c]!='\0')
+ {
+ tex+=buf[c];
+ c++;
+ }
+
+ if (!IsInTexList(tex.c_str()))
+ texlist.push_back(tex);
+
+}
+
+void CMainWnd::InitPaksInDir(const char *folder)
+{
+struct dirent *dirlist;
+DIR *dir;
+
+dir = opendir (g_strBasePath.GetBuffer());
+
+if (dir != NULL)
+{
+ while ((dirlist = readdir (dir)) != NULL)
+ {
+ if (!strstr (dirlist->d_name, ".pk3"))
+ continue;
+
+ std::string path=g_strBasePath.GetBuffer();
+ path+=dirlist->d_name;
+
+ if (IsExcluded(dirlist->d_name))
+ continue;
+
+ CPak *p=new CPak;
+
+ if (p->Open(path.c_str()))
+ paklist.push_back(p);
+ else
+ delete p;
+ }
+ closedir (dir);
+}
+}
+
+void CMainWnd::InitPakList()
+{
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[0]), "Building pk3 references");
+
+ if (g_strModPath.GetLength()>0)
+ InitPaksInDir(g_strModPath.GetBuffer());
+
+ InitPaksInDir(g_strBasePath.GetBuffer());
+
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[0]), "Ready ...");
+}
+
+void CMainWnd::FreePakList()
+{
+ std::list<CPak*>::iterator i;
+
+ for ( i = paklist.begin() ; i != paklist.end() ; i++)
+ {
+ CPak *p=*i;
+ delete p;
+ }
+
+ paklist.clear();
+}
+
+void CMainWnd::DeleteTempFiles()
+{
+ std::list<std::string>::iterator i;
+
+ for ( i = temp_files.begin() ; i != temp_files.end() ; i++)
+ {
+ std::string path=*i;
+
+ remove(path.c_str());
+ }
+
+ temp_files.clear();
+}
+
+bool CMainWnd::IsInTexList(const char *tex)
+{
+ for ( std::list<std::string>::const_iterator i = texlist.begin() ; i != texlist.end() ; i++)
+ {
+ std::string str=*i;
+
+ if (str.compare(tex)==0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void CMainWnd::SkipLines(FILE *fp, int n)
+{
+ for (int c=0 ; c<n ; c++)
+ std::string line=GetLine(fp);
+}
+
+std::string CMainWnd::GetLine(FILE *fp)
+{
+ std::string line;
+ char c='0';
+
+ // grab a line;
+ do
+ {
+ fread(&c,1,1,fp);
+
+ if (c!='\n' && c!='\t' && c!='\r')
+ line+=c;
+
+ } while (c!='\n' && !feof(fp));
+
+ return line;
+
+}
+
+std::string CMainWnd::TrimString(const char *str)
+{
+ std::string out;
+
+ int c=0;
+ while (str[c]==' ' || str[c]=='\t')
+ c++;
+
+ int e=strlen(str);
+ while (str[e]==' ' || str[e]=='\n' || str[e]=='\r')
+ e--;
+
+ out=str;
+ out=out.substr(c,-1);
+
+ return out;
+}
+
+void CMainWnd::ParseShader(const char *pathname, const char *tex)
+{
+ if (pathname==NULL || tex==NULL)
+ return;
+
+ char* shaderbuff;
+ int len = g_FuncTable.m_pfnLoadFile(pathname, (void**)&shaderbuff);
+
+ if (len==0)
+ return;
+
+ std::string shader=shaderbuff;
+
+ // skip to start of shader
+ long pos=shader.find(tex);
+
+ if (pos==-1) // shader not found????
+ return;
+
+ shader=shader.substr(pos);
+
+ bool inStage=FALSE;
+ bool inShader=FALSE;
+
+ // step through the lines
+ while(1)
+ {
+ pos=shader.find_first_of('\n');
+ if (pos==-1)
+ break;
+
+ std::string line=shader.substr(0,pos-1);
+ shader=shader.substr(pos+1,-1);
+
+ line=TrimString(line.c_str());
+
+ if (line=="")
+ continue;
+
+ if (line[0]=='/' && line[1]=='/')
+ continue;
+
+ if (line[0]=='}')
+ {
+ if (!inStage)
+ break;
+ else
+ inStage=FALSE;
+
+ continue;
+ }
+
+ if (line[0]=='{')
+ {
+ if (!inShader)
+ {
+ inShader=TRUE;
+ }
+ else if (inShader)
+ {
+ inStage=TRUE;
+ }
+
+ continue;
+ }
+
+ // check for map/animmap e.t.c;
+
+ if (inStage)
+ {
+
+ if (line.substr(0,3).compare("map")==0)
+ {
+ std::string map=line.substr(4,-1);
+ AddToTexList(map.c_str());
+ continue;
+ }
+
+ if (line.substr(0,8).compare("skyparms")==0)
+ {
+ std::string map=line.substr(9,-1);
+ AddToTexList(map.c_str());
+ continue;
+ }
+
+ if (line.substr(0,8).compare("clampmap")==0)
+ {
+ std::string map=line.substr(9,-1);
+ AddToTexList(map.c_str());
+ continue;
+ }
+
+ if (line.substr(0,7).compare("animmap")==0)
+ {
+ std::string map=line.substr(9,-1);
+ map=TrimString(map.c_str());
+
+ // skip past numbers
+ long p=map.find_first_of(' ');
+ map=map.substr(p+1,-1);
+
+ while (1)
+ {
+ long pos=map.find_first_of(' ');
+
+ if (pos==-1)
+ {
+ AddToTexList(map.c_str());
+ break;
+ }
+
+ std::string tex=map.substr(0,pos);
+ AddToTexList(tex.c_str());
+
+ map=map.substr(pos+1,-1);
+ }
+ }
+ }
+ }
+// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=491
+g_free(shaderbuff);
+}
+
+bool CMainWnd::ProcessFile(const char *nm,const char *suffix,const char *folder)
+{
+ std::string name=nm;
+ name=name.substr(0,name.length()-4);
+ name+=suffix;
+
+ std::string path;
+
+ if (g_strModPath.GetLength()>0)
+ path=g_strModPath.GetBuffer();
+ else
+ path=g_strBasePath.GetBuffer();
+
+ path+=folder;
+ path+="/";
+ path+=name;
+
+ std::string rpath=folder;
+ rpath+="/";
+ rpath+=name;
+
+ if (IsExcluded(rpath.c_str())) // quit out with a FALSE so we try others
+ return FALSE;
+
+ FILE *fp;
+ fp=fopen(path.c_str(),"rb");
+
+ if (fp)
+ {
+ if (!pak.IsInPak(rpath.c_str()))
+ {
+ m_Tree.AddPath(rpath.c_str());
+ pak.AddExternalFile(rpath.c_str(),path.c_str());
+ }
+
+ fclose(fp);
+ return TRUE;
+ }
+ else
+ return FindInAPk3(rpath.c_str());
+
+
+ return FALSE;
+}
+
+bool CMainWnd::ProcessFile(const char *nm)
+{
+ std::string name=nm;
+
+ if (IsExcluded(name.c_str())) // quit out with a TRUE so we think it was added
+ return TRUE;
+
+ std::string path;
+
+ if (g_strModPath.GetLength()>0)
+ path=g_strModPath.GetBuffer();
+ else
+ path=g_strBasePath.GetBuffer();
+
+ path+=name;
+
+ FILE *fp;
+ fp=fopen(path.c_str(),"rb");
+
+ if (fp)
+ {
+ if (!pak.IsInPak(name.c_str()))
+ {
+ m_Tree.AddPath(name.c_str());
+ pak.AddExternalFile(name.c_str(),path.c_str());
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ return FindInAPk3(name.c_str());
+ }
+
+
+ return FALSE;
+}
+
+bool CMainWnd::FindInAPk3(const char *name)
+{
+ std::list<CPak*>::iterator i;
+
+ for ( i = paklist.begin() ; i != paklist.end() ; i++)
+ {
+ CPak *p=*i;
+
+ if (p->m_filename.CompareNoCase(pak.m_filename)==0)
+ continue;
+
+ std::string fname=p->m_filename.GetBuffer();
+ long pos=fname.find_last_of('/');
+ fname=fname.substr(pos+1,-1);
+
+ if (IsExcluded(fname.c_str()))
+ continue;
+
+ if (p->IsInPak(name))
+ {
+ pak_entry pe=p->FindPE(name);
+ m_Tree.AddPath(name);
+ pak.AddPakFile(name,p->m_filename.GetBuffer(),pe.compressed,pe.uncompressed);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void CMainWnd::OnAdd()
+{
+ std::string path;
+ if (g_strModPath.GetLength()>0)
+ path=g_strModPath.GetBuffer();
+ else
+ path=g_strBasePath.GetBuffer();
+
+ //char *filename=file_dialog(TRUE,"Choose a file...",path.c_str(),"*.*");
+ gchar *filename=g_FuncTable.m_pfnFileDialog(m_pMainWnd,TRUE,"Choose a file...",path.c_str(),"pk3man");
+
+ if (filename==NULL)
+ return;
+
+ int c=0;
+
+ while (filename[c]!='\0')
+ {
+ if (filename[c]=='\\')
+ filename[c]='/';
+
+ c++;
+ }
+
+ std::string fname=filename;
+
+ long p=path.length();
+ fname=fname.substr(p,-1);
+
+ if (pak.IsInPak((char*)fname.c_str()))
+ return;
+
+ pak.AddExternalFile((char *)fname.c_str(),(char *)filename);
+ m_Tree.AddPath((char *)fname.c_str());
+
+}
+
+void CMainWnd::OnDelete()
+{
+ std::string path=m_Tree.GetSelected();
+
+ if (path=="")
+ return;
+
+ pak.Delete(path.c_str());
+
+ //m_Tree.RemovePath(path.c_str());
+
+ // dodgy hack but works for now
+ m_Tree.Clear();
+ UpdateTree();
+
+}
+
+void CMainWnd::OnRename()
+{
+ std::string path=m_Tree.GetSelected();
+
+ if (path=="")
+ return;
+
+ CRenameDlg dlg;
+ dlg.m_Name=path;
+
+ if (dlg.DoModal()==IDOK)
+ {
+ pak.Rename(path.c_str(),dlg.m_Name.c_str());
+ m_Tree.Clear();
+ UpdateTree();
+ }
+
+}
+
+void CMainWnd::UpdateTree()
+{
+ // clear the tree and fill it with the items in the pak
+
+ for ( std::list<pak_entry>::const_iterator iEntry = pak.entries.begin() ; iEntry != pak.entries.end() ; iEntry++)
+ {
+ pak_entry pe=*iEntry;
+
+ std::string name;
+ if (pe.rename)
+ name=pe.newname;
+ else
+ name=pe.filename;
+
+ m_Tree.AddPath(name.c_str());
+ }
+
+}
+
+void CMainWnd::OnExtract()
+{
+ std::string path=m_Tree.GetSelected();
+
+ if (path=="")
+ return;
+
+ pak_entry pe=pak.FindPE((char*)path.c_str());
+
+ std::string bpath;
+ if (g_strModPath.GetLength()>0)
+ bpath=g_strModPath.GetBuffer();
+ else
+ bpath=g_strBasePath.GetBuffer();
+
+ //char *pathto=dir_dialog("Extract to ...",bpath.c_str());
+ gchar *pathto=g_FuncTable.m_pfnDirDialog(m_pMainWnd,"Extract to...",bpath.c_str());
+
+ if (pathto==NULL)
+ return;
+
+ long pos=path.find_last_of('/');
+ std::string name=path.substr(pos+1);
+
+ std::string cpath=pathto;
+ cpath+=name;
+
+ if (pe.pakname.compare(pak.m_filename.GetBuffer())==0)
+ {
+ // its in pak
+ pak.ExtractTo((char*)path.c_str(),(char*)cpath.c_str());
+ return;
+ }
+
+}
+
+void CMainWnd::OnView()
+{
+ std::string path=m_Tree.GetSelected();
+
+ if (path=="")
+ return;
+
+ pak_entry pe=pak.FindPE(path.c_str());
+
+ long p=pe.filename.find_last_of('/');
+ std::string name=pe.filename.substr(p+1,-1);
+ std::string temppath=g_strTempPath.GetBuffer()+name;
+
+ if (pe.frompak && pe.pakname.compare(pak.m_filename.GetBuffer())==0)
+ {
+ // its in pak
+ if (!pak.ExtractTo(path.c_str(),(char *)temppath.c_str()))
+ return;
+
+ temp_files.push_back(temppath);
+
+ // view it
+ #ifdef WIN32
+ HANDLE hInst=ShellExecute(NULL,"open",(LPCTSTR)temppath.c_str(),NULL,NULL,SW_SHOWNORMAL);
+ #endif
+
+ return;
+ }
+
+ if (!pe.frompak)
+ {
+#ifdef WIN32
+ HANDLE hInst=ShellExecute(NULL,"open",(LPCTSTR)pe.pathname.c_str(),NULL,NULL,SW_SHOWNORMAL);
+#endif
+ return;
+ }
+
+ if (pe.frompak)
+ {
+ CPak p;
+ if (!p.Open(pe.pakname.c_str()))
+ return;
+
+ if (!p.ExtractTo(path.c_str(),(char *)temppath.c_str()))
+ return;
+
+ temp_files.push_back(temppath);
+
+ // view it
+#ifdef WIN32
+ HANDLE hInst=ShellExecute(NULL,"open",(LPCTSTR)temppath.c_str(),NULL,NULL,SW_SHOWNORMAL);
+#endif
+ return;
+ }
+}
+
+#ifdef WIN32
+void ShortToLong(CString &thePath)
+{
+ WIN32_FIND_DATA wfd;
+ std::string spath=thePath.GetBuffer();
+ std::string lpath="";
+ while(1)
+ {
+ FindFirstFile(spath.c_str(),&wfd);
+ std::string fname=wfd.cFileName;
+ lpath=fname+"\\"+lpath;
+ long pos=spath.find_last_of('\\');
+ spath=spath.substr(0,pos);
+ pos=spath.find_last_of('\\');
+
+ if (pos==-1)
+ {
+ lpath=spath+"\\"+lpath;
+ break;
+ }
+ }
+ thePath=lpath.c_str();
+}
+#endif
+
+void CMainWnd::GetPaths()
+{
+#if 0 //mattn
+#ifdef WIN32
+
+ // convert it to long format
+ ShortToLong(g_strBasePath);
+
+ // mod path
+ if (strcmp(mode,"Quake III Team Arena")==0)
+ {
+ g_strModPath=g_strBasePath;
+ long p=g_strModPath.Find("\\baseq3");
+ g_strModPath=g_strModPath.Left(p);
+ g_strModPath+="\\missionpack\\";
+
+ g_FuncTable.m_pfnQE_ConvertDOSToUnixName ((char *)g_strModPath.GetBuffer(), g_strModPath.GetBuffer());
+ }
+
+ g_FuncTable.m_pfnQE_ConvertDOSToUnixName ((char *)g_strBasePath.GetBuffer(), g_strBasePath.GetBuffer());
+
+ // apppath
+ // get path to the editor
+ char* pBuffer = g_strAppPath.GetBufferSetLength(_MAX_PATH + 1);
+ GetModuleFileName(NULL, pBuffer, _MAX_PATH);
+ pBuffer[g_strAppPath.ReverseFind('\\') + 1] = '\0';
+ g_strAppPath.ReleaseBuffer();
+ g_FuncTable.m_pfnQE_ConvertDOSToUnixName ((char *)g_strAppPath.GetBuffer(), g_strAppPath.GetBuffer());
+
+ char temp_path[_MAX_PATH];
+ GetTempPath(_MAX_PATH,temp_path);
+ g_strTempPath=temp_path;
+
+#endif
+
+#ifdef __linux__
+ Str tmp;
+ tmp = g_strBasePath.GetBuffer();
+ tmp += "/../";
+
+ // NOTE: we build g_strAppPath with a '/' (or '\' on WIN32)
+ // it's a general convention in Radiant to have the slash at the end of directories
+ char real[PATH_MAX];
+ realpath (tmp.GetBuffer(), real);
+ if (real[strlen(real)-1] != '/')
+ strcat(real, "/");
+ g_strAppPath = real;
+
+ g_strTempPath="~/.tmp";
+#endif
+
+ extern const char *PLUGIN_NAME;
+ g_strBitmapsPath = g_FuncTable.m_pfnPathForPluginName(PLUGIN_NAME);
+ g_strBitmapsPath += "bitmaps/";
+#endif
+}
+
+void CMainWnd::UpdateStatus()
+{
+ std::string path=m_Tree.GetSelected();
+
+ if (path=="")
+ {
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[0]), "");
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[1]), "");
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[2]), "");
+
+ return;
+ }
+
+ pak_entry pe=pak.FindPE(path.c_str());
+
+ std::string p=path;
+ long pos=p.find_last_of('/');
+ p=p.substr(pos+1);
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[0]), p.c_str());
+
+ char lbl[255];
+ sprintf(lbl," %ldk ",pe.uncompressed/1000);
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[1]), lbl);
+
+ sprintf(lbl," %ldk ",pe.compressed/1000);
+ gtk_label_set_text (GTK_LABEL (m_pStatusLabel[2]), lbl);
+
+}
+
+void CMainWnd::HandleDrop()
+{
+ std::string path=m_Tree.GetSelected();
+ bool file_selected=TRUE;
+
+ if (path=="")
+ file_selected=FALSE;
+
+ //if (m_mnuDrop == NULL) // first time, load it up
+ //{
+ GtkAccelGroup *accel;//, *menu_in_menu_accel;
+ GtkWidget *menu;//, *menu_in_menu, *item, *submenu;
+
+ menu = m_mnuDrop = gtk_menu_new ();
+ accel = gtk_accel_group_new ();
+ gtk_menu_set_accel_group (GTK_MENU (menu), accel);
+
+ //menu_in_menu = create_menu_in_menu (menu, "Select", accel, &menu_in_menu_accel);
+
+ create_menu_item (menu, "Add file", accel,
+ GTK_SIGNAL_FUNC (HandleCommand), ID_ADD);
+
+ if (file_selected)
+ {
+ create_menu_item (menu, "View File", accel,
+ GTK_SIGNAL_FUNC (HandleCommand), ID_VIEW);
+
+ create_menu_item (menu, "Extract file", accel,
+ GTK_SIGNAL_FUNC (HandleCommand), ID_EXTRACT);
+
+ menu_separator (menu);
+
+ create_menu_item (menu, "Rename File", accel,
+ GTK_SIGNAL_FUNC (HandleCommand), ID_RENAME);
+
+ create_menu_item (menu, "Delete file", accel,
+ GTK_SIGNAL_FUNC (HandleCommand), ID_DELETE);
+ }
+ //}
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME);
+}
+
+
+////////////////////////////////////////////
+// some handy stuff ripped from GtkRadiant
+////////////////////////////////////////////
+
+
+GtkWidget* create_menu_in_menu (GtkWidget *menu, gchar *label, GtkAccelGroup *menu_accel,
+ GtkAccelGroup **submenu_accel)
+{
+ GtkWidget *item, *submenu;
+ guint tmp_key;
+
+ item = gtk_menu_item_new_with_label ("");
+ tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (item)->child), label);
+ gtk_widget_add_accelerator (item, "activate_item", menu_accel, tmp_key, 0, (GtkAccelFlags)0);
+ gtk_widget_show (item);
+ gtk_container_add (GTK_CONTAINER (menu), item);
+
+ submenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+ *submenu_accel = gtk_accel_group_new ();
+ gtk_menu_set_accel_group (GTK_MENU (submenu), *submenu_accel);
+
+ return submenu;
+}
+
+GtkWidget* create_menu_item (GtkWidget *menu, gchar *label, GtkAccelGroup *menu_accel,
+ GtkSignalFunc func, int id)
+{
+ GtkWidget *item;
+ guint tmp_key;
+
+#ifdef NO_UNDERSCORE
+ char label_tmp[1024];
+ strcpy( label_tmp, label );
+ if (char* c = strchr (label_tmp, '_'))
+ while (*c)
+ *c++ = *(c+1);
+ item = gtk_menu_item_new_with_label (label_tmp);
+#else
+ item = gtk_menu_item_new_with_label ("");
+ tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (item)->child), label);
+ gtk_widget_add_accelerator (item, "activate_item", menu_accel, tmp_key, 0, (GtkAccelFlags)0);
+#endif
+ gtk_widget_show (item);
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
+
+
+ return item;
+}
+
+GtkWidget* menu_separator (GtkWidget *menu)
+{
+ GtkWidget *menu_item = gtk_menu_item_new ();
+ gtk_menu_append (GTK_MENU (menu), menu_item);
+ gtk_widget_set_sensitive (menu_item, FALSE);
+ gtk_widget_show (menu_item);
+ return menu_item;
+}