/* * frend * frend.cc * Copyright (C) 1998-2004 David Nordlund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * For further details, please read the included COPYING file, * or go to http://www.gnu.org/copyleft/gpl.html */ //#define GTK_DISABLE_DEPRECATED 1 #include #include "frend.h" #include #include #include #include #include #include const char*fr_AppName; GtkTooltips *fr_Tooltips; bool fr_initialized = false; const gchar* fr_Stock_to_Gtk_Stock(fr_StockItem s, const gchar*fallback=0) { switch(s) { case FR_STOCK_OK: return GTK_STOCK_OK; case FR_STOCK_CANCEL: return GTK_STOCK_CANCEL; case FR_STOCK_APPLY: return GTK_STOCK_APPLY; case FR_STOCK_CLOSE: return GTK_STOCK_CLOSE; case FR_STOCK_OPEN: return GTK_STOCK_OPEN; case FR_STOCK_SAVE: return GTK_STOCK_SAVE; case FR_STOCK_SAVE_AS: return GTK_STOCK_SAVE_AS; case FR_STOCK_NEW: return GTK_STOCK_NEW; case FR_STOCK_ADD: return GTK_STOCK_ADD; case FR_STOCK_REMOVE: return GTK_STOCK_REMOVE; case FR_STOCK_YES: return GTK_STOCK_YES; case FR_STOCK_NO: return GTK_STOCK_NO; case FR_STOCK_REFRESH: return GTK_STOCK_REFRESH; case FR_STOCK_CONFIG: return GTK_STOCK_PREFERENCES; case FR_STOCK_EDIT: return GTK_STOCK_INDEX; case FR_STOCK_ABOUT: return GTK_STOCK_DIALOG_INFO; case FR_STOCK_RUN: return GTK_STOCK_EXECUTE; case FR_STOCK_CLEAR: return GTK_STOCK_CLEAR; case FR_STOCK_DELETE: return GTK_STOCK_DELETE; case FR_STOCK_QUIT: return GTK_STOCK_QUIT; default: std::cerr << "got invalid fr_StockItem: " << (int)s << ", fallback=" << (void*)fallback << std::endl; } return fallback; } /* ############################### fr_ArgList ############################# */ /// Create an ArgList of initial size s fr_ArgList::fr_ArgList(int s) { ArgAlloc(s); } /// Create an ArgList from the string args fr_ArgList::fr_ArgList(const char*args) { int s = 2; for(int i=0; args[i]; i++) if(args[i]<=' ') s++; ArgAlloc(s); std::istringstream a(args); a >> *this; } /// Create an ArgList from an array of strings fr_ArgList::fr_ArgList(int argc, char*argv[]) { ArgAlloc(argc); for(int a=0; a= 0) ArgAlloc(n); else size = 0; } /// Set the case sensitivity of forthcoming Args fr_ArgList& fr_ArgList::operator<< (const fr_CaseSensitivity cs) { casesensitivity = cs; return *this; } /// Add a char* argument to the list fr_ArgList& fr_ArgList::operator<< (const char*arg) { if (!arg) return *this; if (size-countargs<1) Grow(); if(ignore) { ignore--; return *this; } int s = strlen(arg) + 1; char *a = new char[s]; strcpy(a, arg); argv[countargs] = a; sensitive[countargs] = casesensitivity; marked[countargs] = false; argv[++countargs] = 0; return *this; } /// Add an int to the list fr_ArgList& fr_ArgList::operator<< (int i) { std::ostringstream s; s << i; return *this << s.str().c_str(); } /// @return a pointer to the Nth entry in an ArgList const char* fr_ArgList::operator[] (int n) const { if( (n < 0) || (n >= countargs) ) return (char*)0; return argv[n]; } /** * Check to see if S is an entry in this ArgList * skip over marked entries * if a match is found, mark it * @return the index of the match, -1 for no match */ int fr_ArgList::MatchArg(const char*S, fr_CaseSensitivity cs) { if((!S)||(S[0]==0)) return -1; for(int a=0, match=0; a=0) && (n=0) && (n> (std::istream& i, fr_ArgList& a) { std::string buf; std::getline(i, buf); buf += '\n'; std::string arg(""); char c, quoted = 0; bool escaped = false, exists = false; std::string::iterator b = buf.begin(), e = buf.end(); do { if(b==e) { if(escaped||quoted) { // yup, just in case newlines in filenames are the next big thing. *sigh* if(!std::getline(i, buf)) throw "premature end of file"; buf += "\n"; b = buf.begin(); e = buf.end(); } else break; } c = *b++; if(quoted) { if(escaped) { if(!strchr("\\\"", c)) arg += '\\'; arg += c; escaped = false; } else if(c==quoted) { quoted = false; exists = true; } else if((c=='\\')&&(quoted=='"')) escaped = true; else arg += c; } else if(escaped) { arg += c; escaped = false; } else if(isspace(c) || (c=='#')) { if(exists || arg.size()) a << arg; if(c=='#') break; arg = ""; exists = false; } else if(c=='\\') escaped = true; else if((c=='"')||(c=='\'')) quoted = c; else arg += c; } while(true); return i; } /// encode and write an arg list to an ostream std::ostream& operator<< (std::ostream& o, const fr_ArgList& a) { return o << a.GetString() << std::endl; } /* ############################ fr_dimension ############################## */ template void fr_dimension::input(std::istream& i) { std::string buf, buf2; i >> buf; unsigned int l = buf.size(), xpos = 0, ypos = 0; if(l < 3) return i; if(buf[0]=='(') xpos++; if(buf[l-1]==',') { i >> buf2; buf += buf2; ypos = l; } else { ypos = buf.find(',', xpos+1); if(ypos == buf.npos) ypos = xpos; else ypos++; } const char*buf3 = buf.c_str(); _x = strtol(&(buf[xpos]), NULL, 0); _y = strtol(&(buf[ypos]), NULL, 0); return i; } template void fr_dimension::print(std::ostream& o) const { return o << "(" << _x << ", " << _y << ")"; } /* ############################## fr_Color ################################ */ std::string fr_Color::toString() const { char buf[10]; snprintf(buf, 8, "#%06X", rgb); return buf; } /* ############################## fr_Image ################################ */ fr_Image::fr_Image(fr_StockItem s) { GtkWidget *i = gtk_image_new(); pixbuf = gtk_widget_render_icon(i, fr_Stock_to_Gtk_Stock(s), GTK_ICON_SIZE_BUTTON, NULL); gtk_object_sink(GTK_OBJECT(i)); } fr_Image::fr_Image(FILE*fptr, size_t filesize) { size_t defaultsize=4096; size_t bytesleft = filesize?filesize:1; size_t bufsize = filesize?(std::min(bytesleft, defaultsize)):defaultsize; if(!fptr) throw "fr_Image: NULL FILE pointer"; guchar *buf = new guchar[bufsize]; GdkPixbufLoader* loader = gdk_pixbuf_loader_new(); while(bytesleft) { size_t bufleft = filesize?(std::min(bytesleft, bufsize)):bufsize; size_t r = fread(buf, 1, bufleft, fptr); if(r >= 0) { if(filesize) bytesleft -= r; if(!gdk_pixbuf_loader_write(loader, buf, r, NULL)) break; } else break; } pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); gdk_pixbuf_loader_close(loader, NULL); delete[] buf; if(!pixbuf) throw "fr_Image: could not load image"; } fr_Image::~fr_Image() { //g_object_unref(pixbuf); } int fr_Image::getWidth() const { if(pixbuf) return gdk_pixbuf_get_width(pixbuf); return 0; } int fr_Image::getHeight() const { if(pixbuf) return gdk_pixbuf_get_height(pixbuf); return 0; } GtkWidget*fr_Image::MakeGtkWidget() const { GtkWidget *W = gtk_image_new_from_pixbuf(pixbuf); gtk_widget_show(W); return W; } /* ############################## fr_Element ############################## */ /// Create an empty element fr_Element::fr_Element() { Name = ""; GUI = Element = (GtkWidget*)0; Listeners = 0; } /// Create an empty element with a parent fr_Element::fr_Element(fr_Element* parent, const char*name): Parent(parent) { Element = GUI = (GtkWidget*)0; if (Parent) Window = Parent->Window; Listeners = 0; SetName(name); } /// Create an element that is just an image fr_Element::fr_Element(fr_Element*parent, fr_Image& I): Parent(parent) { if (Parent) Window = Parent->Window; Element = GUI = I.MakeGtkWidget(); Listeners = 0; SetVisibility(true); } /// Create a custom element from a gtk widget fr_Element::fr_Element(fr_Element*parent, GtkWidget*custom): Parent(parent) { Name = ""; Element = GUI = custom; if (Parent) Window = Parent->Window; SetVisibility(true); Listeners = 0; } /// @return a pointer to the gtk widget representing this element GtkWidget*fr_Element::getGUI() { if (GUI) return GUI; return Element; } /// Create the tool-tip for this element void fr_Element::CreateTooltip(const std::string& tip) { if(!fr_Tooltips) fr_Tooltips = gtk_tooltips_new(); gtk_tooltips_set_tip(fr_Tooltips, Element, tip.c_str(), 0); } /// Assign a name to this element void fr_Element::SetName(const char*name) { Name = name; } /// Assign a tool-tip to this element void fr_Element::SetTooltip(const std::string& tip) { CreateTooltip(tip); } /// Enable/Disable the ability to use this element void fr_Element::SetEditable(bool s) { if (Element) gtk_widget_set_sensitive(Element, s); if (GUI) gtk_widget_set_sensitive(GUI, s); } /// Suggest a size for this element to take up void fr_Element::SetSize(int w, int h) { GtkWidget *W = getGUI(); if (!W) return; gtk_widget_set_size_request(W, w, h); } /// get the size of this widget fr_Size fr_Element::getSize() const { GtkRequisition r; gtk_widget_size_request(GUI, &r); return fr_Size(r.width, r.height); } /// Show / Hide this element void fr_Element::SetVisibility(bool s) { if (!Element) g_error("Whoah, I can't show nothing!"); if (!GUI) GUI = Element; if (s) { gtk_widget_show(Element); gtk_widget_show(GUI); } else { gtk_widget_hide(Element); gtk_widget_hide(GUI); } } /// Focus input on this element void fr_Element::GrabFocus() { gtk_widget_grab_focus(Element); if(GTK_WIDGET_CAN_DEFAULT(Element)) gtk_widget_grab_default(Element); } /// If this element is a container, give it some contents void fr_Element::Contain(fr_Element*C) { gtk_container_add(GTK_CONTAINER(Element), C->getGUI()); } /// Add a label to the left of this element from string L void fr_Element::AddLabel(const char*L) { GtkWidget *lbl = gtk_label_new(L); gtk_widget_show(lbl); if(!getGUI()) { GUI = Element = lbl; return; } GtkWidget *box = gtk_hbox_new(false, 1); gtk_box_pack_start(GTK_BOX(box), lbl, false, false, 1); gtk_box_pack_start(GTK_BOX(box), getGUI(), true, true, 1); gtk_widget_show(box); GUI = box; } /// Add a label to the left of this element based on Label L void fr_Element::AddLabel(fr_Label*L) { GtkWidget *box = gtk_hbox_new(false, 1); gtk_box_pack_start(GTK_BOX(box), L->getGUI(), false, false, 1); if(getGUI()) gtk_box_pack_start(GTK_BOX(box), getGUI(), true, true, 1); gtk_widget_show(box); GUI = box; } /** * Set up the event handler for this element * If this element receives an event, * notify the EventHandler in the parent. */ void fr_Element::AddListener(fr_Listener*L) { if (!L) return; if (!Listeners) Listeners = new std::list (); Listeners->push_back(L); } /// Remove an event listener for this object void fr_Element::RemoveListener(fr_Listener*L) { if ((!L) || (!Listeners)) return; std::list::iterator i; for(i=Listeners->begin(); i!=Listeners->end(); i++) if(*i == L) Listeners->erase(i); if(Listeners->empty()) { delete Listeners; Listeners = 0; } } /* ############################# fr_Event ################################# */ fr_Event::fr_Event(fr_Element*E, int t, int ia, void*arg): Element(E), Type(t), intArg(ia), Arg(arg) { if(!(E->Listeners)) return; std::list::iterator i; for(i=E->Listeners->begin(); i!=E->Listeners->end(); i++) (*i)->EventOccurred(this); } bool fr_Event::Is(fr_Element* E, int t) { if(Element == E) { if(t<0) return true; else return Type==t; } return false; } /* ############################# fr_Listener ############################## */ void fr_Listener::Click(GtkObject*, fr_Element*E) { fr_Event(E, fr_Click); } void fr_Listener::Changed(GtkObject*, fr_Element*E) { fr_Event(E, fr_Changed); } void fr_Listener::MenuClick(GtkObject*, fr_Element*E) { fr_Event(E, fr_MenuClick); } /* ############################# fr_Separator ############################# */ /// Create a new bar (just a divider) fr_Separator::fr_Separator(fr_Element*parent, fr_Direction Dir): fr_Element(parent) { if(Dir==fr_Vertical) GUI = Element = gtk_vseparator_new(); else GUI = Element = gtk_hseparator_new(); SetVisibility(true); } /* ############################# fr_Box ################################## */ /// Create a new box fr_Box::fr_Box(fr_Element*parent, const char*name): fr_Element(parent, name) { SetStretch(Normal, Normal); SetPadding(2, 2); } /// Use a fixed size area for this box void fr_Box::SetSpaceSize(int w, int h) { GUI = Element = gtk_fixed_new(); SetSize(w, h); fr_Element::SetVisibility(true); } /// Use a table for this box, and set the size void fr_Box::SetGridSize(int w, int h, bool SameSize) { GUI = Element = gtk_table_new(h, w, SameSize); SizeX = w; SizeY = h; PosX = PosY = 0; SetStretch(Normal, Normal); fr_Element::SetVisibility(true); } /// Set the padding to be put around elements added to this box void fr_Box::SetPadding(int x, int y) { PadX = x; PadY = y; } /// Specify the stretching mechanism to be used with elements added to this box void fr_Box::SetStretch(fr_StretchMode x, fr_StretchMode y) { StretchX = x; StretchY = y; ax = ay = (GtkAttachOptions)(GTK_FILL|GTK_EXPAND); if (x==Shrink) ax = GTK_SHRINK; else if(x==Grow) ax = GTK_EXPAND; else if(x==Fill) ax = GTK_FILL; if (y==Shrink) ay = GTK_SHRINK; else if(y==Grow) ay = GTK_EXPAND; else if(y==Fill) ay = GTK_FILL; } /** * Put a frame aroud this box. * If the box has been named (fr_Element::SetName), the frame will * have the boxes namenear the upper left corner */ void fr_Box::Frame() { if (!GUI) return; GtkWidget *F = gtk_frame_new(Name); //gtk_container_border_width(GTK_CONTAINER(F), 4); gtk_container_add(GTK_CONTAINER(F), GUI); gtk_widget_show(F); GUI = F; } /// Add a 3D border around this box, Beveled Out void fr_Box::AddBorderBevelOut() { if (!GUI) return; GtkWidget *B = gtk_frame_new(0); gtk_container_add(GTK_CONTAINER(B), GUI); gtk_frame_set_shadow_type(GTK_FRAME(B), GTK_SHADOW_OUT); gtk_widget_show(B); GUI = B; } /// Add a 3D border around this box, Beveled In void fr_Box::AddBorderBevelIn() { if (!GUI) return; GtkWidget *B = gtk_frame_new(0); gtk_container_add(GTK_CONTAINER(B), GUI); gtk_frame_set_shadow_type(GTK_FRAME(B), GTK_SHADOW_IN); gtk_widget_show(B); GUI = B; } /** * Add an element to this box. * It will be placed horizontally between x1 and x2 * It will be placed vertically between y1 and y2 */ void fr_Box::Pack(fr_Element& E, int x1, int y1, int x2, int y2) { GtkWidget*G = E.getGUI(); if(!G) { g_error("Packing a non-existant Element into table!"); return; } if(!Element) { g_error("Packing Element into non-existant table!"); return; } /* if((StretchX == Normal)&&(StretchY == Normal)) gtk_table_attach_defaults(GTK_TABLE(Element), G, x1, x2, y1, y2); else*/ gtk_table_attach(GTK_TABLE(Element), G, x1, x2, y1, y2, ax, ay, PadX, PadY); PosX = x2; PosY = y1; if(PosX >= SizeX) { PosX = 0; PosY++; } Contents.insert(&E); } /** * Add an element to this box. * It will start in the next table cell after the last element added * (or the first cell if it is the first item) * and will take up dx cells across and dy cells down */ void fr_Box::Pack(fr_Element& E, int dx, int dy) { Pack(E, PosX, PosY, PosX+dx, PosY+dy); } /** * Add a widget to this box, * placing it at exactly x,y pixels from the upper-left corner * of the box */ bool fr_Box::Place(GtkWidget*w, int x, int y) { if(!w) return false; GtkWidget*P = w->parent; if(P==Element) gtk_fixed_move(GTK_FIXED(Element), w, x, y); else if(!P) gtk_fixed_put(GTK_FIXED(Element), w, x, y); else return false; gtk_widget_show(w); return true; } /** * Add an element to this box, * placing it at exactly x,y pixels from the upper-left corner * of the box */ void fr_Box::Place(fr_Element& E, int x, int y) { if((Place(E.getGUI(), x, y))&&(Contents.count(&E)==0)) Contents.insert(&E); else std::cerr << "Cannot Place Element (" << E.GetName() << ") in " << Name << " box. " << "It already belongs somewhere else." << std::endl; } /// Remove an item from this box void fr_Box::Remove(fr_Element& E) { if(Contents.count(&E)==1) { GtkWidget *G = E.getGUI(); gtk_widget_ref(G); gtk_container_remove(GTK_CONTAINER(G->parent), G); gtk_widget_unparent(G); Contents.erase(&E); } } /// Remove all contents from this box void fr_Box::RemoveAll() { ContentList::iterator i; for(i=Contents.begin(); i!=Contents.end(); i++) Remove(*(*i)); } /* ############################# fr_Label ################################ */ /// Create a label fr_Label::fr_Label(fr_Element*parent, const char*label): fr_Element(parent, label) { GUI = Element = gtk_label_new(Name); SetVisibility(true); } /// Assign new text to this label void fr_Label::SetLabel(const char*label) { Name = label; gtk_label_set_text(GTK_LABEL(Element), Name); } /* ############################# fr_Button ################################ */ /// Create a new button with a string for a label fr_Button::fr_Button(fr_Element*parent, const char*label, fr_Image*I): fr_Element(parent, label) { img = 0; Element = (GtkWidget*)0; if(!I) { if(strlen(label)<21) { char BtnStr[32]; snprintf(BtnStr, 32, "gtk-%s", label); for(int i=4; BtnStr[i]; i++) BtnStr[i] = tolower(BtnStr[i]); if(strcmp(BtnStr, "gtk-default")==0) { img = gtk_image_new_from_stock(GTK_STOCK_HOME, GTK_ICON_SIZE_BUTTON); gtk_widget_show(img); } else if(strstr(GTK_STOCK_OK GTK_STOCK_CANCEL GTK_STOCK_YES GTK_STOCK_NO GTK_STOCK_NEW GTK_STOCK_OPEN GTK_STOCK_CLOSE GTK_STOCK_APPLY GTK_STOCK_SAVE GTK_STOCK_HELP GTK_STOCK_COPY GTK_STOCK_CUT GTK_STOCK_PASTE GTK_STOCK_DELETE GTK_STOCK_FIND GTK_STOCK_HOME GTK_STOCK_CLEAR, BtnStr)) GUI = Element = gtk_button_new_from_stock(BtnStr); } } if(!Element) { GUI = gtk_hbox_new(false, false); gtk_widget_show(GUI); GtkWidget *l = gtk_label_new(Name); gtk_widget_show(l); if(I) img = I->MakeGtkWidget(); if(img) gtk_box_pack_start(GTK_BOX(GUI), img, false, true, 1); gtk_box_pack_start(GTK_BOX(GUI), l, false, true, 1); Element = gtk_button_new(); gtk_container_add(GTK_CONTAINER(Element), GUI); GUI = Element; } SetVisibility(true); } /// Create a new button with a label from an fr_Label fr_Button::fr_Button(fr_Element*parent, fr_Label*label): fr_Element(parent, label->GetName()) { img = 0; Element = gtk_button_new(); Contain(label); SetVisibility(true); } /// Create a new button with a stock icon and a label fr_Button::fr_Button(fr_Element*parent, fr_StockItem b, const std::string& label): fr_Element(parent, label.c_str()) { const char*gtkstock = fr_Stock_to_Gtk_Stock(b); if(gtkstock) { if(!label.size()) Element = gtk_button_new_from_stock(gtkstock); else { GtkWidget *hbox, *img, *lbl; img = gtk_image_new_from_stock(gtkstock, GTK_ICON_SIZE_BUTTON); lbl = gtk_label_new(label.c_str()); hbox = gtk_hbox_new(false, 3); gtk_box_pack_start_defaults(GTK_BOX(hbox), img); gtk_box_pack_start_defaults(GTK_BOX(hbox), lbl); Element = gtk_button_new(); gtk_widget_show_all(hbox); gtk_container_add(GTK_CONTAINER(Element), hbox); } } else if(label.size()) Element = gtk_button_new_with_label(label.c_str()); else // this should never happen :) Element = gtk_button_new_with_label("Out of stock"); GUI = Element; SetVisibility(true); } void fr_Button::AddListener(fr_Listener*L) { if(!Listeners) g_signal_connect(G_OBJECT(Element), "clicked", G_CALLBACK(fr_Listener::Click), this); fr_Element::AddListener(L); } /* ############################# fr_CustomButton ######################### */ fr_CustomButton::fr_CustomButton(fr_Element*parent, const char*name): fr_Element(parent, name), bgpixbuf(0), bg_x_offset(0), bg_y_offset(0), Up(0), Dn(0), IsPressed(false), MouseInside(false) { GUI = Element = gtk_event_box_new(); Box = gtk_vbox_new(0, 0); gtk_widget_show(Box); gtk_container_add(GTK_CONTAINER(Element), Box); Up = gtk_label_new(name); gtk_widget_show(Up); gtk_container_add(GTK_CONTAINER(Box), Up); SetVisibility(true); } void fr_CustomButton::setBgImg(const fr_Image*bg, int bg_x, int bg_y) { bg_x_offset = bg_x; bg_y_offset = bg_y; if(bgpixbuf) { g_object_unref(G_OBJECT(bgpixbuf)); bgpixbuf = (GdkPixbuf*)0; } if(bg) { bgpixbuf = bg->pixbuf; g_object_ref(G_OBJECT(bgpixbuf)); } } void fr_CustomButton::setUpImg(const fr_Image*I) { GdkPixbuf *btn; if(Up) gtk_container_remove(GTK_CONTAINER(Box), Up); if(I) { int w = I->getWidth(), h = I->getHeight(); if(bgpixbuf) { btn = gdk_pixbuf_copy(gdk_pixbuf_new_subpixbuf(bgpixbuf, bg_x_offset, bg_y_offset, w, h)); gdk_pixbuf_composite(I->pixbuf, btn, 0, 0, w, h, 0, 0, 1, 1, GDK_INTERP_NEAREST, 0xff); Up = gtk_image_new_from_pixbuf(btn); g_object_unref(G_OBJECT(btn)); } else Up = gtk_image_new_from_pixbuf(I->pixbuf); gtk_widget_show(Up); gtk_container_add(GTK_CONTAINER(Box), Up); } else Up = 0; SetState(IsPressed); } void fr_CustomButton::setDnImg(const fr_Image*I) { GdkPixbuf *btn; if(Dn) gtk_container_remove(GTK_CONTAINER(Box), Dn); if(I) { int w = I->getWidth(), h = I->getHeight(); if(bgpixbuf) { btn = gdk_pixbuf_copy(gdk_pixbuf_new_subpixbuf(bgpixbuf, bg_x_offset, bg_y_offset, w, h)); gdk_pixbuf_composite(I->pixbuf, btn, 0, 0, w, h, 0, 0, 1, 1, GDK_INTERP_NEAREST, 0xff); Dn = gtk_image_new_from_pixbuf(btn); g_object_unref(G_OBJECT(btn)); } else Dn = gtk_image_new_from_pixbuf(I->pixbuf); gtk_widget_show(Dn); gtk_container_add(GTK_CONTAINER(Box), Dn); } else Dn = 0; SetState(IsPressed); } void fr_CustomButton::AddListener(fr_Listener*L) { if(!Listeners) { gtk_widget_set_events(Element, GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE |GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK); g_signal_connect (G_OBJECT(Element), "button_press_event", G_CALLBACK(ButtonPressed), this); g_signal_connect (G_OBJECT(Element), "button_release_event", G_CALLBACK(ButtonReleased), this); g_signal_connect (G_OBJECT(Element), "enter_notify_event", G_CALLBACK(MouseOver), this); g_signal_connect (G_OBJECT(Element), "leave_notify_event", G_CALLBACK(MouseOff), this); } fr_Element::AddListener(L); } void fr_CustomButton::SetState(bool Pressed) { if(Pressed) { if((Up)&&(Dn)) { gtk_widget_hide(Up); gtk_widget_show(Dn); } } else { if(Dn) gtk_widget_hide(Dn); if(Up) gtk_widget_show(Up); } } void fr_CustomButton::ButtonPressed(GtkObject*, void*, fr_CustomButton*B) { if(B) { B->SetState(true); B->IsPressed = true; } } void fr_CustomButton::ButtonReleased(GtkObject*, void*, fr_CustomButton*B) { if(B) { if(B->MouseInside && B->IsPressed) fr_Event(B, fr_Click); B->SetState(false); B->IsPressed = false; } } void fr_CustomButton::MouseOver(GtkObject*, void*, fr_CustomButton*B) { if(B && !B->MouseInside) { if(B->IsPressed) B->SetState(true); B->MouseInside = true; } } void fr_CustomButton::MouseOff(GtkObject*, void*, fr_CustomButton*B) { if(B) { B->SetState(false); B->MouseInside = false; } } /* ############################# fr_ButtonBox ############################ */ fr_ButtonBox::fr_ButtonBox(fr_Element*parent, fr_Direction dir): fr_Element(parent) { Element = GUI = (dir == fr_Vertical)? gtk_vbutton_box_new():gtk_hbutton_box_new(); gtk_button_box_set_layout(GTK_BUTTON_BOX(Element), GTK_BUTTONBOX_END); SetVisibility(true); } /// Add a button to the button box void fr_ButtonBox::AddButton(fr_Button& btn, bool dflt) { GtkWidget *b = btn.getGUI(); GTK_WIDGET_SET_FLAGS(b, GTK_CAN_DEFAULT); /*if((dflt)&&(GTK_WIDGET_CAN_DEFAULT(b))) gtk_widget_grab_default(b);*/ gtk_box_pack_start(GTK_BOX(Element), b, true, true, 0); } /* ############################# fr_Option ############################### */ /// Create a new option fr_Option::fr_Option(fr_Element*parent, const char*name): fr_Element(parent, name) { KeyStroke = (const char*)0; Enabled = true; } /** * Associate a keystroke with this option * (this is not an accelerator for the frend-based GUI) */ void fr_Option::SetKeyStroke(const char*K) { KeyStroke = K; } /// Assign a tool-tip to this option void fr_Option::SetTooltip(const std::string& tip) { std::string t(tip); const char *arg = Args.GetPreferredArg(); if(arg) { t += "\noption: "; t += arg; } if(KeyStroke) { t += "\nkey: "; t += KeyStroke; } CreateTooltip(t); } /// Enable/Disable the ability to use this option void fr_Option::SetEditable(bool s) { if(!Enabled) s=false; if(Element) gtk_widget_set_sensitive(Element, s); if(GUI) gtk_widget_set_sensitive(GUI, s); } /// If enable is false, set the option to its default value and disable it void fr_Option::EnableIf(bool cond) { if((Enabled)&&(!cond)) SetToDefault(); Enabled = cond; SetEditable(cond); } /* ############################# fr_Toggle ############################### */ /// Create a new Toggle element fr_Toggle::fr_Toggle(fr_Element*parent, const char*name): fr_Option(parent, name) { DefaultState = false; } fr_Toggle::~fr_Toggle() { //std::cerr << "~fr_Toggle(\"" << (Name?Name:"") << "\");" << std::endl; } /// Specify the default state of this toggle item void fr_Toggle::SetDefaultState(bool s) { DefaultState = s; } /// Assign a tool-tip to this toggle element void fr_Toggle::SetTooltip(const std::string& tip) { std::string t(tip); const char *arg = Args.GetPreferredArg(); if(arg) { t += "\noption(on): "; t += arg; } arg = NotArgs.GetPreferredArg(); if(arg) { t += "\noption(off): "; t += arg; } t += "\nDefault: "; t += DefaultState?"on":"off"; if(KeyStroke) { t += "\nkey: "; t += KeyStroke; } CreateTooltip(t); } /** * Given an ArgList, see if any args belong to this toggle element, * if they do, they will be marked, and the toggle state set accordingly */ void fr_Toggle::SiftArgs(fr_ArgList& L) { if(Args.MatchArgs(L)>=0) SetState(true); if(NotArgs.MatchArgs(L)>=0) SetState(false); } /// Set the state of this toggle element void fr_Toggle::SetState(bool s) { if(Enabled) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Element), s); } /// @return the state of this toggle element bool fr_Toggle::GetState() { return GTK_TOGGLE_BUTTON(Element)->active; } /// Set the state of this toggle element to its default state void fr_Toggle::SetToDefault() { SetState(DefaultState); } /// @return true if this toggle element is in its default state bool fr_Toggle::IsDefault() { return (GetState() == DefaultState); } /** * @return a command-line argument that reflects the state of this element * @return 0 if it is in its default state */ const char*fr_Toggle::GetActiveArg() { if (IsDefault()) return (const char*)0; return DefaultState? NotArgs.GetPreferredArg() : Args.GetPreferredArg(); } /** * Add to an ArgList a command-line argument that reflects the current state * of this toggle element. * Nothing is added if it is in its default state */ void fr_Toggle::CompileArgs(fr_ArgList& L) { if(Enabled) L << GetActiveArg(); } void fr_Toggle::AddListener(fr_Listener*L) { if(!Listeners) g_signal_connect(G_OBJECT(Element), "toggled", G_CALLBACK(fr_Listener::Click), this); fr_Element::AddListener(L); } /* ############################# fr_Checkbox ############################## */ /// Create a new checkbox, with a label and a default state fr_Checkbox::fr_Checkbox(fr_Element*parent, const char*label, bool s): fr_Toggle(parent, label) { GUI = Element = gtk_check_button_new_with_label(label); SetDefaultState(s); SetVisibility(true); } /* ############################# fr_ToggleInGroup ######################### */ fr_ToggleInGroup::fr_ToggleInGroup(fr_GroupHolder*parent, const char*name): fr_Toggle(parent, name) { Next = 0; } fr_ToggleInGroup::~fr_ToggleInGroup() { //std::cerr << "~fr_ToggleInGroup(\"" << (Name?Name:"") << "\");" << std::endl; //if (Next) delete Next; } // This function does not make sense in a group context, so do nothing void fr_ToggleInGroup::SetToDefault() { } /* ############################# fr_GroupHolder ########################### */ /// Create a new GroupHolder fr_GroupHolder::fr_GroupHolder(fr_Element*parent, const char*name): fr_Option(parent, name) { First = Last = 0; DefaultIndex = GroupSize = 0; Group = 0; } fr_GroupHolder::~fr_GroupHolder() { //delete First; } /// Activate element i in this group void fr_GroupHolder::SelectIndex(int i) { fr_ToggleInGroup *P = First; for(int x=0; P; x++, P=P->Next) P->SetState(x==i); } /// @return a pointer to the selected element in this group fr_ToggleInGroup*fr_GroupHolder::GetSelected() { fr_ToggleInGroup *P; for(P=First; P; P=P->Next) if(P->GetState()) return P; return (fr_ToggleInGroup*)0; } /// @return the index of the selected element in this group int fr_GroupHolder::GetIndex() { fr_ToggleInGroup *P = First; for(int i=0; P; i++, P=P->Next) if(P->GetState()) return i; return -1; } /// @return true if the selected element is the default element bool fr_GroupHolder::IsDefault() { return (GetIndex() == DefaultIndex); } /// Select the default element void fr_GroupHolder::SetToDefault() { SelectIndex(DefaultIndex); } /// Given an ArgList, scan for args belonging to elements in this group void fr_GroupHolder::SiftArgs(fr_ArgList& L) { fr_ToggleInGroup *P; for(P=First; P; P=P->Next) P->SiftArgs(L); } /// Add to L the an arg reflecting the current state of the selected element void fr_GroupHolder::CompileArgs(fr_ArgList& L) { fr_ToggleInGroup *P; if(!Enabled) return; if(IsDefault()) return; if(!(P = GetSelected())) return; P->CompileArgs(L); } /* ############################# fr_RadioButton ########################### */ /// Create a new radio button with a label fr_RadioButton::fr_RadioButton(fr_RadioGroup*parent, const char*label): fr_ToggleInGroup(parent, label) { GUI = Element = gtk_radio_button_new_with_label(parent->Group, label); SetVisibility(true); parent->AddItem(this); } /* ############################# fr_RadioGroup ############################ */ /// Create a group for adding radio buttons of a single group fr_RadioGroup::fr_RadioGroup(fr_Element*parent, const char*label, bool box): fr_GroupHolder(parent, label) { Group = 0; if(box) { Element = gtk_hbox_new(false, 3); GUI = gtk_frame_new(label); gtk_container_add(GTK_CONTAINER(GUI), Element); SetVisibility(true); } else Element = GUI = (GtkWidget*)0; } /// Add a radio button to this group void fr_RadioGroup::AddItem(fr_RadioButton*NewItem) { if (!NewItem) return; else GroupSize++; if (!First) First = NewItem; else Last->Next = NewItem; Last = NewItem; if(Element) gtk_box_pack_start(GTK_BOX(Element), Last->GUI, true, false, 3); Group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(Last->Element)); } /* ############################# fr_MenuItem ############################## */ /// Create a Menu item with a label fr_MenuItem::fr_MenuItem(fr_PulldownMenu*parent, const char*label): fr_ToggleInGroup(parent, label) { GUI = Element = gtk_radio_menu_item_new_with_label(parent->Group, label); //gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(Element), true); parent->Group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(Element)); SetVisibility(true); parent->AddItem(this); } /// @return true if this menu option is selected bool fr_MenuItem::GetState() { return GTK_CHECK_MENU_ITEM(Element)->active; } /// Scan ArgList L for args that match this menu item void fr_MenuItem::SiftArgs(fr_ArgList& L) { if(Args.MatchArgs(L)>=0) ((fr_PulldownMenu*)Parent)->SelectItem(this); } /* ############################ fr_PulldownMenu ########################### */ /// Create a new pull-down menu with a title (label) fr_PulldownMenu::fr_PulldownMenu(fr_Element*parent, const char*label): fr_GroupHolder(parent, label) { GUI = Element = gtk_option_menu_new(); Menu = gtk_menu_new(); Group = 0; gtk_option_menu_set_menu(GTK_OPTION_MENU(GUI), Menu); gtk_widget_show(GUI); GtkWidget *W = gtk_menu_item_new_with_label(label); gtk_widget_show(W); gtk_menu_shell_append(GTK_MENU_SHELL(Menu), W); W = gtk_separator_menu_item_new(); gtk_widget_show(W); gtk_menu_shell_append(GTK_MENU_SHELL(Menu), W); gtk_option_menu_set_history(GTK_OPTION_MENU(GUI), 0); GroupSize = 0; SetVisibility(true); } /// Add a MenuItem to this pull down menu void fr_PulldownMenu::AddItem(fr_MenuItem*NewItem) { if (!NewItem) return; GroupSize++; if (!First) First = NewItem; else Last->Next = NewItem; Last = NewItem; gtk_menu_shell_append(GTK_MENU_SHELL(Menu), NewItem->GUI); } /// Select by index a MenuItem in this pull-down menu void fr_PulldownMenu::SelectIndex(int i) { fr_ToggleInGroup *P = First; for(int x=0; P; x++,P=P->Next) if(i==x) { SelectItem(P); break; } } /// Select by matching a pointer a menu item in this pull-down menu void fr_PulldownMenu::SelectItem(fr_ToggleInGroup*I) { if(!Enabled) return; fr_ToggleInGroup *P = First; for(int i=0; P; i++,P=P->Next) if(P==I) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(P->Element), true); //gtk_option_menu_set_history(GTK_OPTION_MENU(GUI), i+2); break; } } /// Select the default item in this pull-down menu void fr_PulldownMenu::SetToDefault() { SelectItem(First); gtk_option_menu_set_history(GTK_OPTION_MENU(GUI), 0); } void fr_PulldownMenu::AddListener(fr_Listener*L) { if(!Listeners) g_signal_connect(G_OBJECT(Element), "value_changed", G_CALLBACK(fr_Listener::Click), this); fr_Element::AddListener(L); } /* ############################# fr_Adjustment ############################ */ /** * Create an adjustment element with a * Default Value, minimum value, maximum value, and number of * decimal places to use */ fr_Adjustment::fr_Adjustment(fr_Element*parent, const char*name, float DV, float min, float max, int DP): fr_Option(parent, name) { DefaultValue = DV; DecimalPlaces = DP; Minimum = min; Maximum = max; snprintf(Format, sizeof(Format), "%%.%df", DecimalPlaces); Step = 1; for(int setStep=0; setStepvalue; } /// @return in a string the value of this adjustment const char*fr_Adjustment::GetFormattedValue() { snprintf(Printed, sizeof(Printed), Format, GetValue()); return Printed; } /// Reset the default value of this adjustment void fr_Adjustment::ResetDefault(float DV) { bool WasDefault = IsDefault(); bool WasEnabled = Enabled; Enabled = true; DefaultValue = DV; if (WasDefault) SetToDefault(); Enabled = WasEnabled; } /// Set the value of this adjustment to its default value void fr_Adjustment::SetToDefault() { SetValue(DefaultValue); } /// @return true if this adjustment is at its default value bool fr_Adjustment::IsDefault() { if(ABS(GetValue() - DefaultValue) < Step) return true; return false; } /// Assign a tool-tip for this adjustment void fr_Adjustment::SetTooltip(const std::string& tip) { char deftip[32], rangefmt[64], rangetip[64]; std::string t(tip); const char *arg = Args.GetPreferredArg(); if(arg) { t += "\noption: "; t += arg; t += " "; } t += "\nDefault: "; snprintf(deftip, sizeof(deftip), Format, DefaultValue); t += deftip; snprintf(rangefmt, sizeof(rangefmt), "%s, %s", Format, Format); snprintf(rangetip, sizeof(rangetip), rangefmt, Minimum, Maximum); t += "\nRange: [ "; t += rangetip; t += " ]"; if(KeyStroke) { t += "\nkey: "; t += KeyStroke; } CreateTooltip(t); } /// Scan ArgList L for args that belong to this asjustment void fr_Adjustment::SiftArgs(fr_ArgList& L) { int m = Args.MatchArgs(L) + 1; if (m<1) return; const char *S = L[m]; if(S) { L.Mark(m); SetValue(atof(S)); } else std::cerr << "Missing numeric value after \"" << L[m-1] << '"' << std::endl; } /// Put in ArgList L args that reflect the current value of this adjustment void fr_Adjustment::CompileArgs(fr_ArgList& L) { if(!Enabled) return; if (IsDefault()) return; L << Args.GetPreferredArg() << GetFormattedValue(); } void fr_Adjustment::AddListener(fr_Listener*L) { if(!Listeners) g_signal_connect(G_OBJECT(Adj), "value_changed", G_CALLBACK(fr_Listener::Changed), this); fr_Element::AddListener(L); } /* ############################# fr_Slider ################################ */ /** * Create a new slider with a label, direction, default value, min, max, * number of decimal places to use, and whether or not to display a value * next to the slider */ fr_Slider::fr_Slider(fr_Element*parent, const char*label, fr_Direction dir, float DV, float min, float max, int DP, bool ShowDigits): fr_Adjustment(parent, label, DV, min, max+1, DP) { Maximum--; if(dir==fr_Horizontal) GUI = Element = gtk_hscale_new(GTK_ADJUSTMENT(Adj)); else GUI = Element = gtk_vscale_new(GTK_ADJUSTMENT(Adj)); gtk_scale_set_digits(GTK_SCALE(Element), DP); gtk_scale_set_draw_value(GTK_SCALE(Element), ShowDigits); AddLabel(label); SetVisibility(true); } /* ############################# fr_NumEntry ############################## */ /** * Create a new text entry area for a numeric value, with a default value, * min & max values, and number of decimal places to use. */ fr_NumEntry::fr_NumEntry(fr_Element*parent, const char*label, float DV, float min, float max, int DP): fr_Adjustment(parent, label, DV, min, max, DP) { GUI = Element = gtk_spin_button_new(GTK_ADJUSTMENT(Adj), 0, DP); AddLabel(label); SetVisibility(true); } /* ############################# fr_Text ################################## */ /** * Create a one line text box, with a label, default string, * Case sensitive value for matching, and maximum length for value */ fr_Text::fr_Text(fr_Element*parent, const char*label, const std::string& DefaultStr, bool Case, int MaxLen): fr_Option(parent, label) { GUI = Element = gtk_entry_new(); DefaultText = DefaultStr; CaseSensitive = Case; if((label)&&(label[0])) { Name = label; AddLabel(label); } if((MaxLen > 0)&&(MaxLen < 0x10001)) gtk_entry_set_max_length(GTK_ENTRY(Element), MaxLen); SetVisibility(true); } /// Set the string in this text element void fr_Text::SetText(const std::string& t) { if (Enabled) gtk_entry_set_text(GTK_ENTRY(Element), t.c_str()); } /// @return the text for this text entry const std::string fr_Text::GetText() { return gtk_entry_get_text(GTK_ENTRY(Element)); } /// Select the text in this text entry void fr_Text::SelectText() { gtk_editable_select_region(GTK_EDITABLE(Element), 0, -1); } /// Set the text field to its default value void fr_Text::SetToDefault() { gtk_entry_set_text(GTK_ENTRY(Element), DefaultText.c_str()); } /// @return true if the current text matches the default text bool fr_Text::IsDefault() { std::string t = GetText(); bool eq; if(CaseSensitive) eq = equal(t.begin(), t.end(), DefaultText.begin(), stringeqcs()); else eq = equal(t.begin(), t.end(), DefaultText.begin(), stringeqci()); return eq; } /// Scan ArgList L for args that belong to this text entry void fr_Text::SiftArgs(fr_ArgList& L) { int m = Args.MatchArgs(L) + 1; if (m<1) return; const char *S = L[m]; if(S) { L.Mark(m); SetText(S); } else std::cerr << "Missing text data after \"" << L[m-1] << '"' << std::endl; } /// Put in ArgList L args that reflect the value of this text entry void fr_Text::CompileArgs(fr_ArgList& L) { if(!Enabled) return; const char *S = gtk_entry_get_text(GTK_ENTRY(Element)); if((!S)||(!S[0])) return; L << Args.GetPreferredArg() << S; } void fr_Text::AddListener(fr_Listener*L) { if(!Listeners) { g_signal_connect(G_OBJECT(Element), "activate", G_CALLBACK(fr_Listener::Click), this); g_signal_connect(G_OBJECT(Element), "changed", G_CALLBACK(fr_Listener::Changed), this); } fr_Element::AddListener(L); } /* ############################# fr_DataTable ############################# */ fr_DataTable::fr_DataTable(fr_Element*parent, int cols, bool sngl): fr_Element(parent), Rows(0), Columns(cols) { Element = gtk_clist_new(Columns); GUI = gtk_scrolled_window_new(0, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(GUI), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(GUI), Element); SetSingle(sngl); gtk_clist_set_shadow_type(GTK_CLIST(Element), GTK_SHADOW_IN); SetVisibility(true); } fr_DataTable::~fr_DataTable() { } void fr_DataTable::SetHeaders(const char**Headers) { for(int c=0; c=Columns)) return; SortColumn = col; gtk_clist_set_sort_column(GTK_CLIST(Element), SortColumn); } void fr_DataTable::SetSortOnInsert(bool s, bool ascend) { gtk_clist_set_sort_column(GTK_CLIST(Element), SortColumn); gtk_clist_set_sort_type(GTK_CLIST(Element), ascend?GTK_SORT_ASCENDING:GTK_SORT_DESCENDING); gtk_clist_set_auto_sort(GTK_CLIST(Element), s); //std::cerr << "auto_sort is " << s << std::endl; } int fr_DataTable::AddRow(const char**RowData, int row) { int r, c; gchar**rowdata = new gchar*[Columns]; for(c=0; c < Columns; c++) rowdata[c] = g_strdup(RowData[c]); if(row>=0) r = gtk_clist_insert(GTK_CLIST(Element), row, rowdata); else r = gtk_clist_append(GTK_CLIST(Element), rowdata); Rows++; for(c=0; c < Columns; c++) g_free(rowdata[c]); delete[] rowdata; return r; } void fr_DataTable::RemoveRow(int row) { gtk_clist_remove(GTK_CLIST(Element), row); Rows--; } void fr_DataTable::AssociateData(int row, void*data) { gtk_clist_set_row_data(GTK_CLIST(Element), row, data); } void*fr_DataTable::AssociatedData(int row) { return gtk_clist_get_row_data(GTK_CLIST(Element), row); } void fr_DataTable::Deselect(int row) { gtk_clist_unselect_row(GTK_CLIST(Element), row, 0); } void fr_DataTable::DeselectAll() { gtk_clist_unselect_all(GTK_CLIST(Element)); } void fr_DataTable::Select(int row) { gtk_clist_select_row(GTK_CLIST(Element), row, 0); } const std::string fr_DataTable::GetCell(int row, int col) { char *t; gtk_clist_get_text(GTK_CLIST(Element), row, col, &t); return std::string(t); } const fr_Image* fr_DataTable::GetCellPic(int row, int col) { GdkPixmap *p; GdkBitmap *b; gtk_clist_get_pixmap(GTK_CLIST(Element), row, col, &p, &b); if(!p) return (fr_Image*)0; ImageCollection::iterator i, e = imgcache.end(); for(i=imgcache.begin(); i!=e; i++) if(i->second->first == p) return i->first; return (fr_Image*)0; } void fr_DataTable::SetCell(int row, int col, const std::string& txt, const fr_Image*I, bool showboth) { if(!I) gtk_clist_set_text(GTK_CLIST(Element), row, col, txt.c_str()); else { ImageCollection::iterator f = imgcache.find(I), e = imgcache.end(); ImageData *imgd; if(f==e) { imgd = new ImageData; gdk_pixbuf_render_pixmap_and_mask(I->pixbuf, (GdkPixmap**)(&(imgd->first)), (GdkBitmap**)(&(imgd->second)), 0x80); imgcache[I] = imgd; } else imgd = f->second; if(!showboth) gtk_clist_set_pixmap(GTK_CLIST(Element), row, col, (GdkPixmap*)imgd->first, (GdkBitmap*)imgd->second); else gtk_clist_set_pixtext(GTK_CLIST(Element), row, col, txt.c_str(), 3, (GdkPixmap*)imgd->first, (GdkBitmap*)imgd->second); } } void fr_DataTable::AllowReorder(bool s) { gtk_clist_set_reorderable(GTK_CLIST(Element), s); } void fr_DataTable::SetRowHeight(int h) { gtk_clist_set_row_height(GTK_CLIST(Element), h); } void fr_DataTable::SetColumnWidth(int col, int w) { if((col<0)||(col>=Columns)) return; if(w<0) gtk_clist_set_column_auto_resize(GTK_CLIST(Element), col, true); else gtk_clist_set_column_width(GTK_CLIST(Element), col, w); } void fr_DataTable::SetColumnAlign(int col, Alignment a) { switch(a) { case Left: gtk_clist_set_column_justification(GTK_CLIST(Element), col, GTK_JUSTIFY_LEFT); return; case Right: gtk_clist_set_column_justification(GTK_CLIST(Element), col, GTK_JUSTIFY_RIGHT); return; case Center: gtk_clist_set_column_justification(GTK_CLIST(Element), col, GTK_JUSTIFY_CENTER); return; } } void fr_DataTable::Freeze(bool f) { if(f) gtk_clist_freeze(GTK_CLIST(Element)); else gtk_clist_thaw(GTK_CLIST(Element)); } int fr_DataTable::CountSelectedRows() { return g_list_length(GTK_CLIST(Element)->selection); } int fr_DataTable::GetSelected(int nth) { int *rp = (int*)g_list_nth(GTK_CLIST(Element)->selection, nth); if (rp) return *rp; return -1; } bool fr_DataTable::IsRowSelected(int row) { int *r; for(int i=CountSelectedRows(); i>0; --i) { r = (int*)g_list_nth(GTK_CLIST(Element)->selection, i); if((*r)==row) return true; } return false; } void fr_DataTable::ShowRow(int row) { gtk_clist_moveto(GTK_CLIST(Element), row, 0, 0.0, 0.0); } void fr_DataTable::RemoveAll() { gtk_clist_clear(GTK_CLIST(Element)); Rows = 0; } void fr_DataTable::ReplaceRow(const char**RowData, int row) { for(int c=Columns; c; --c) gtk_clist_set_text(GTK_CLIST(Element), row, c, RowData[c]); } void fr_DataTable::SetSingle(bool s) { gtk_clist_set_selection_mode(GTK_CLIST(Element), s ? GTK_SELECTION_SINGLE : GTK_SELECTION_EXTENDED); } void fr_DataTable::Sort(bool ascending) { gtk_clist_set_sort_column(GTK_CLIST(Element), SortColumn); gtk_clist_set_sort_type(GTK_CLIST(Element), ascending? GTK_SORT_ASCENDING:GTK_SORT_DESCENDING); gtk_clist_sort(GTK_CLIST(Element)); } void fr_DataTable::AddListener(fr_Listener*L) { if(!Listeners) { g_signal_connect (G_OBJECT(Element), "select_row", G_CALLBACK(EventRowSelected), this); g_signal_connect (G_OBJECT(Element), "unselect_row", G_CALLBACK(EventRowUnselected), this); g_signal_connect (G_OBJECT(Element), "click_column", G_CALLBACK(EventColSelected), this); } fr_Element::AddListener(L); } void fr_DataTable::EventRowSelected(GtkWidget*W, int row, int col, void*event, fr_DataTable*D) { fr_Event(D, fr_Select, row); if((event)&&(((GdkEventButton*)event)->type==GDK_2BUTTON_PRESS)) fr_Event(D, fr_DoubleClick, row); } void fr_DataTable::EventRowUnselected(GtkWidget*W, int row, int col, void*, fr_DataTable*D) { fr_Event(D, fr_Unselect, row); } void fr_DataTable::EventColSelected(GtkWidget*W, int col, fr_DataTable*D) { fr_Event(D, fr_Click, col); } /* ########################## fr_TextArea ############################### */ fr_TextArea::fr_TextArea(fr_Element*parent, const char*name): fr_Box(this, name), fgtag(0), bgtag(0), char_width(8), char_height(16) { char*fontstr; PangoContext *pangocontext; PangoFont *pangofont; PangoFontDescription *fontdesc; PangoRectangle fontextents; SetGridSize(1, 1, false); text = gtk_text_view_new(); GUI = gtk_scrolled_window_new(0, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(GUI), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(GUI), Element); gtk_widget_show(GUI); //gtk_text_view_set_top_margin(GTK_TEXT_VIEW(text), 3); gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 3); //gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(text), 3); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 3); fontstr = getenv("FR_MONOFONT"); if(!fontstr) fontstr = "monospace 10"; fontdesc = pango_font_description_from_string(fontstr); pangocontext = gtk_widget_get_pango_context(text); pangofont = pango_context_load_font(pangocontext, fontdesc); pango_font_get_glyph_extents(pangofont, 'S', NULL, &fontextents); char_width = PANGO_PIXELS(fontextents.width); char_height = PANGO_PIXELS(fontextents.height); char_width = std::max(6, std::min(char_width, 33)); char_height = std::max(6, std::min(char_height, 33)); gtk_widget_modify_font(text, fontdesc); pango_font_description_free(fontdesc); gtk_table_attach_defaults(GTK_TABLE(Element), text, 0, 1, 0, 1); gtk_widget_show(text); SetEditable(false); SetVisibility(true); } void fr_TextArea::SetEditable(bool e) { gtk_text_view_set_editable(GTK_TEXT_VIEW(text), e); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), e); } void fr_TextArea::setSizeForText(int cols, int rows) { SetSize(++cols * char_width, ++rows * char_height); } void fr_TextArea::clear() { GtkTextBuffer* buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); GtkTextIter start, end; gtk_text_buffer_get_start_iter(buf, &start); gtk_text_buffer_get_end_iter(buf, &end); gtk_text_buffer_delete(buf, &start, &end); } void fr_TextArea::print(const std::string& t) { GtkTextBuffer* buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); GtkTextIter end; gtk_text_buffer_get_end_iter(buf, &end); gtk_text_buffer_insert_with_tags(buf, &end, t.c_str(), -1, fgtag, bgtag, NULL); } void fr_TextArea::print(int i) { std::ostringstream s; s << i; print(s.str()); } void fr_TextArea::println(const std::string& t) { print(t + "\n"); } void fr_TextArea::setDefaultColors(const fr_Colors& c) { GdkColor c_fg, c_bg; fr_Color fg, bg; if(!c.size()) return; fg = c.front(); if(c.size() == 1) bg = -fg; else bg = c.back(); gdk_color_parse(fg.toString().c_str(), &c_fg); gdk_color_parse(bg.toString().c_str(), &c_bg); gtk_widget_modify_base(text, GTK_STATE_NORMAL, &c_bg); gtk_widget_modify_base(text, GTK_STATE_INSENSITIVE, &c_bg); gtk_widget_modify_base(text, GTK_STATE_SELECTED, &c_fg); gtk_widget_modify_text(text, GTK_STATE_NORMAL, &c_fg); gtk_widget_modify_text(text, GTK_STATE_INSENSITIVE, &c_fg); gtk_widget_modify_text(text, GTK_STATE_SELECTED, &c_bg); setColor(c); } void fr_TextArea::setColor(const fr_Colors& c) { GtkTextBuffer* buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); int n = c.size(); if(!n) return; std::string s, k; if(n > 0) { const fr_Color &fg(c.front()); s = fg.toString(); k = "fg:" + s; fgtag = tagcache[k]; if(!fgtag) { fgtag = gtk_text_buffer_create_tag(buf, NULL, "foreground", s.c_str(), NULL); tagcache[k] = fgtag; } } if(n > 1) { const fr_Color &bg(c.back()); s = bg.toString(); k = "bg:" + s; bgtag = tagcache[k]; if(!bgtag) { bgtag = gtk_text_buffer_create_tag(buf, NULL, "background", s.c_str(), NULL); tagcache[k] = bgtag; } } } /* ############################# fr_File ################################## */ /// Create a new file selection box with a browse button fr_File::fr_File(fr_Element*parent, const char*title, const char*ENVvar, const std::string& Path, bool dironly): fr_Option(parent, title), BrowseButton(this, "..."), OK(this, "Ok"), Cancel(this, "Cancel"), DirOnly(dironly) { Element = gtk_entry_new(); gtk_widget_show(Element); DefaultPath = Path; DefaultEnv = ENVvar?g_strdup(ENVvar):0; if(title) BrowseStr = title; else BrowseStr = ""; gtk_widget_destroy(BrowseButton.getGUI()); GtkWidget *BrowsePixmap = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON); gtk_widget_show(BrowsePixmap); BrowseButton.GUI = BrowseButton.Element = gtk_button_new(); gtk_container_add(GTK_CONTAINER(BrowseButton.GUI), BrowsePixmap); BrowseButton.SetVisibility(true); if(BrowseStr.size()) BrowseButton.SetTooltip(BrowseStr); BrowseButton.AddListener(this); FileSelect = 0; OK.AddListener(this); Cancel.AddListener(this); GUI = gtk_table_new(1, 5, false); gtk_table_attach_defaults(GTK_TABLE(GUI), Element, 0,4,0,1); gtk_table_attach(GTK_TABLE(GUI), BrowseButton.getGUI(), 4,5,0,1, GTK_SHRINK, GTK_SHRINK, 2, 0); gtk_widget_show(GUI); SetVisibility(true); } fr_File::~fr_File() { //std::cerr << "~fr_File(\"" << (Name?Name:"") << "\");" << std::endl; if(DefaultEnv) g_free(DefaultEnv); } /** * Get the default path to use in the file text entry. * If the DefaultEnv environtment variable is set, it will use that, * if not, it will use DefaultPath */ std::string fr_File::GetDefaultPath() const { if((DefaultEnv)&&(DefaultEnv[0]!=0)) { char*E = getenv(DefaultEnv); if((E)&&(E[0])) return E; } return DefaultPath; } /// @return true if the file selected is the default path bool fr_File::IsDefault() const { return (GetFileName()==DefaultPath); } /// Set the file selected to the default path void fr_File::SetToDefault() { SetFileName(DefaultPath); } /// Set the tooltip file the file entry field void fr_File::SetTooltip(const std::string& tip) { std::string t(tip); const char *arg = Args.GetPreferredArg(); if(arg) { t += "\noption: "; t += arg; t += (DirOnly?" ":" "); } if(DefaultPath.size()) { t += "\nDefault: "; t += DefaultPath; } if((DefaultEnv)&&(DefaultEnv[0])) { t += "\nEnv: $"; t += DefaultEnv; } if(KeyStroke) { t += "\nkey: "; t += KeyStroke; } CreateTooltip(t); } /// Bring up a file browser for navigating a file system to find a file void fr_File::FilePopup() { if ((FileSelect)||(!Enabled)) return; FileSelect = gtk_file_selection_new(BrowseStr.size()?BrowseStr.c_str():"Browse"); g_signal_connect (G_OBJECT(GTK_FILE_SELECTION(FileSelect)->ok_button), "clicked", G_CALLBACK(fr_Listener::Click), &OK); g_signal_connect (G_OBJECT(GTK_FILE_SELECTION(FileSelect)->cancel_button), "clicked", G_CALLBACK(fr_Listener::Click), &Cancel); g_signal_connect (G_OBJECT(FileSelect), "destroy", G_CALLBACK(fr_Listener::Click), &Cancel); std::string FN = GetFileName(); if (FN.size() == 0) FN = GetDefaultPath(); if(DirOnly) { gtk_widget_set_sensitive(GTK_WIDGET(GTK_FILE_SELECTION(FileSelect)->file_list), false); if(FN.size() && FN[FN.size()-1] != '/') FN += '/'; } gtk_file_selection_set_filename(GTK_FILE_SELECTION(FileSelect), FN.c_str()); gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(FileSelect)); SetEditable(false); //disable editing of Element & BrowseButton gtk_widget_show(FileSelect); } /// Callback for when the OK button in ::PopUp is pressed void fr_File::FileOK() { SetFileName(gtk_file_selection_get_filename(GTK_FILE_SELECTION(FileSelect))); gtk_widget_grab_focus(Element); gtk_widget_destroy(FileSelect); FileSelect=0; SetEditable(true); } /// Callback for when the Cancel button in ::Popup is pressed void fr_File::FileCanceled() { gtk_widget_destroy(FileSelect); FileSelect=0; SetEditable(true); } /// Set the file name to use void fr_File::SetFileName(std::string filename) { if(!Enabled) return; if(DirOnly && filename.size() && filename[filename.size()-1] != '/') filename += '/'; gtk_entry_set_text(GTK_ENTRY(Element), filename.c_str()); } /// @return the selected file name std::string fr_File::GetFileName() const { return gtk_entry_get_text(GTK_ENTRY(Element)); } /// Scan ArgList L for args that belong to this file void fr_File::SiftArgs(fr_ArgList& L) { int m = Args.MatchArgs(L) + 1; if(m<1) return; const char *S = L[m]; if(S) { L.Mark(m); SetFileName(S); } else std::cerr << "Missing filename after \"" << L[m-1] << '"' << std::endl; } /// Add to ArgList L args that reflect the selected file void fr_File::CompileArgs(fr_ArgList& L) { if(!Enabled) return; if(IsDefault()) return; L << Args.GetPreferredArg() << GetFileName(); } /// Handle events from the ::Popup file browser void fr_File::EventOccurred(fr_Event*e) { if (e->Is(BrowseButton)) FilePopup(); else if(e->Is(OK)) FileOK(); else if(e->Is(Cancel)) FileCanceled(); } /* ############################# fr_Notebook ############################## */ /// Create a new notebook fr_Notebook::fr_Notebook(fr_Element*parent, const char*name): fr_Box(parent, name), current_page(0) { GUI = Element = gtk_notebook_new(); gtk_notebook_set_scrollable(GTK_NOTEBOOK(Element), true); gtk_notebook_popup_enable(GTK_NOTEBOOK(Element)); // std::cout << "fr_Notebook@" << this << ".Element = " << Element << std::endl; g_signal_connect(G_OBJECT(Element), "switch-page", G_CALLBACK(fr_Notebook::notebook_page_changed), this); SetVisibility(true); } /// Add a page to this notebook void fr_Notebook::AddPage(fr_Notepage& P) { GtkWidget*newpage = P.getGUI(); Contents.insert(&P); // std::cout << "fr_Notebook@" << this << "::AddPage(" << P.Element << ");" << std::endl; gtk_notebook_append_page(GTK_NOTEBOOK(Element), newpage, P.TabContainer); gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(Element), newpage, P.GetName()); } void fr_Notebook::notebook_page_changed(GtkWidget*gnb,GtkWidget*, unsigned int pn, fr_Notebook*b) { if(!b || !gnb) return; GtkWidget *p = gtk_notebook_get_nth_page(GTK_NOTEBOOK(gnb), pn); // std::cerr << "Turn to page " << (void*)p << " in " << (void*)b << std::endl; fr_Notebook::ContentList::iterator i = b->Contents.begin(), e = b->Contents.end(); if(b->current_page) { // std::cerr << " - changed from " << b->current_page->GetName(); b->current_page->pageChanged(false); } for(; i!=e; i++) { fr_Notepage *n = (fr_Notepage*)(*i); if(p==n->getGUI()) { n->pageChanged(true); // std::cerr << " - to " << n->GetName(); b->current_page = n; break; } } } /* ############################# fr_Notepage ############################## */ /// Create a new Notepage fr_Notepage::fr_Notepage(fr_Notebook*parent, const char*TabLabel): fr_Box(parent, TabLabel), icon_visible(true), label_visible(true) { TabContainer = gtk_hbox_new(false, false); tab_label = gtk_label_new(TabLabel); tab_icon = 0; gtk_widget_show(tab_label); gtk_box_pack_end_defaults(GTK_BOX(TabContainer), tab_label); gtk_widget_show(TabContainer); } fr_Notepage::~fr_Notepage() { //std::cerr << "~fr_Notepage(\"" << (Name?Name:"") << "\");" << std::endl; } void fr_Notepage::setIcon(fr_Image*I) { if(tab_icon) { gtk_container_remove(GTK_CONTAINER(TabContainer), tab_icon); //gtk_widget_destroy(tab_icon); //for some reason, this is a bad idea. tab_icon = (GtkWidget*)0; } if(I) { tab_icon = I->MakeGtkWidget(); gtk_box_pack_end_defaults(GTK_BOX(TabContainer), tab_icon); //setLabelVisibility(false); } // else // setLabelVisibility(true); if(!icon_visible && tab_icon) gtk_widget_hide(tab_icon); } void fr_Notepage::setLabelVisibility(bool v) { label_visible = v; if(!tab_label) return; if(v) gtk_widget_show(tab_label); else gtk_widget_hide(tab_label); } void fr_Notepage::setIconVisibility(bool v) { icon_visible = v; if(!tab_icon) return; if(v) gtk_widget_show(tab_icon); else gtk_widget_hide(tab_icon); } void fr_Notepage::pageChanged(bool to_me) { // std::cout << "fr_Notepage[" << Name << "]::pageChanged(" << to_me << ");" << std::endl; } // ############################# fr_Window ################################ // void fr_Window::WindowInit() { MenuBar = gtk_menu_bar_new(); Vbox = gtk_vbox_new(0, 0); gtk_widget_show(Vbox); gtk_box_pack_start(GTK_BOX(Vbox), MenuBar, false, false, 0); } /// Create a new window fr_Window::fr_Window(fr_Element*parent, const char*Title): fr_Box(parent, Title) { WindowInit(); Window = gtk_window_new(GTK_WINDOW_TOPLEVEL); SetName(Title); gtk_widget_realize(Window); gtk_container_add(GTK_CONTAINER(Window), Vbox); } fr_Window::~fr_Window() { //std::cerr << "~fr_Window(\"" << (Name?Name:"") << "\");" << std::endl; //if(Window) gtk_widget_destroy(Window); } /// Set the table size for this window void fr_Window::SetGridSize(int w, int h, bool SameSize) { fr_Box::SetGridSize(w, h, SameSize); gtk_box_pack_start(GTK_BOX(Vbox), GUI, true, true, 0); } /// Set the space size for this window void fr_Window::SetSpaceSize(int w, int h) { fr_Box::SetSpaceSize(w, h); gtk_fixed_set_has_window(GTK_FIXED(GUI), true); gtk_box_pack_start(GTK_BOX(Vbox), GUI, true, true, 0); } void fr_Window::setResizable(bool r) { gtk_window_set_resizable(GTK_WINDOW(Window), r); } /** * Name this window. * The name will also be used by the window manager */ void fr_Window::SetName(const char*name) { fr_Element::SetName(name); if(Window) gtk_window_set_title(GTK_WINDOW(Window), name); } /// Assign an icon to this window void fr_Window::setIcon(const fr_Image* I) { gtk_window_set_icon(GTK_WINDOW(Window), I?I->pixbuf:0); } void fr_Window::setBackdrop(const fr_Image*I) { if(I) gdk_pixbuf_render_pixmap_and_mask(I->pixbuf, &backdrop, NULL, 1); else backdrop = 0; gdk_window_set_back_pixmap(Element->window, backdrop, false); } /// Add a menu bar to the top of this window void fr_Window::AddMenu(const char*label, fr_MenuBarItem Items[]) { GtkWidget *Menu, *MenuItem, *MenuLabel; Menu = gtk_menu_new(); for(int i=0; Items[i].Label; i++) { if(strcmp(Items[i].Label, "-")) { if(Items[i].Icon > 0) { MenuItem = gtk_menu_item_new(); GtkWidget *hbox = gtk_hbox_new(false, false); GtkWidget *pix = Items[i].Icon->MakeGtkWidget(); gtk_widget_show(pix); gtk_box_pack_start(GTK_BOX(hbox), pix, false, false, 1); GtkWidget *mlbl = gtk_label_new(Items[i].Label); gtk_widget_show(mlbl); gtk_box_pack_start(GTK_BOX(hbox), mlbl, false, false, 1); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(MenuItem), hbox); } else MenuItem = gtk_menu_item_new_with_label(Items[i].Label); } else { MenuItem = gtk_menu_item_new(); gtk_widget_set_sensitive(MenuItem, false); } gtk_widget_show(MenuItem); gtk_menu_shell_append(GTK_MENU_SHELL(Menu), MenuItem); if(Items[i].Element) g_signal_connect (G_OBJECT(MenuItem), "activate", G_CALLBACK(fr_Listener::MenuClick), Items[i].Element); } MenuItem = gtk_menu_item_new(); MenuLabel = gtk_label_new(label); gtk_widget_show(MenuLabel); gtk_container_add(GTK_CONTAINER(MenuItem), MenuLabel); gtk_widget_show(MenuItem); gtk_menu_item_set_submenu(GTK_MENU_ITEM(MenuItem), Menu); if(strcmp(label, "Help")==0) gtk_menu_item_set_right_justified(GTK_MENU_ITEM(MenuItem), true); gtk_menu_shell_append(GTK_MENU_SHELL(MenuBar), MenuItem); gtk_widget_show(MenuBar); } /// return true if window is visible bool fr_Window::isVisible() const { return gdk_window_is_visible(Window->window); } /// Show/Hide this window void fr_Window::SetVisibility(bool s) { fr_Element::SetVisibility(s); if(s) { gtk_window_resize(GTK_WINDOW(Window), 1, 1); gtk_window_set_position(GTK_WINDOW(Window), GTK_WIN_POS_CENTER); gtk_widget_show(Window); gdk_window_raise(Window->window); } else gtk_widget_hide(Window); } /// Save the upper-left window co-ordinates void fr_Window::SaveWindowPos() { gdk_window_get_root_origin(Window->window, &pos_x, &pos_y); } /// Move the window to the saved position void fr_Window::RestoreWindowPos() { gdk_window_move(Window->window, pos_x, pos_y); } void fr_Window::Mesg(const std::string& msg, bool err) { if(err) std::cerr << msg << std::endl; GtkWidget *msgwin; msgwin = gtk_message_dialog_new (GTK_WINDOW(Window), GTK_DIALOG_DESTROY_WITH_PARENT, err?GTK_MESSAGE_ERROR:GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, msg.c_str()); g_signal_connect_swapped (G_OBJECT (msgwin), "response", G_CALLBACK (gtk_widget_destroy), G_OBJECT (msgwin)); if(!isVisible()) gtk_window_set_position(GTK_WINDOW(msgwin), GTK_WIN_POS_MOUSE); gtk_widget_show(msgwin); } void fr_Window::AddListener(fr_Listener*L) { if(!Listeners) { g_signal_connect (G_OBJECT(Window), "destroy", G_CALLBACK(EventDestroy), this); g_signal_connect (G_OBJECT(Window), "delete_event", G_CALLBACK(EventDelete), this); } fr_Element::AddListener(L); } void fr_Window::EventDestroy(GtkObject*, fr_Window*W) { fr_Event(W, fr_Destroy); } bool fr_Window::EventDelete(GtkObject*, GdkEvent*, fr_Window*W) { fr_Event(W, fr_Destroy); return true; } /* ############################# fr_MainProgram ########################### */ /** * Set up the main program. * This class should be inherited, and used in main(). */ fr_MainProgram::fr_MainProgram(const char*name, const fr_ArgList& args): fr_Window((fr_Element*)0, name), commandlineargs(args) { } int fr_MainProgram::run() { SetToDefaults(); SiftArgs(commandlineargs); gtk_main(); return exitcode; } void fr_MainProgram::exit(int exit_code) { exitcode = exit_code; gtk_main_quit(); } /* ############################# Misc. Functions ########################## */ /// Give a message to the user of this program. void fr_Mesg(const std::string& Message, bool err) { GtkWidget *Window; if(err) std::cerr << Message << std::endl; #ifdef FR_GNOME Window = gnome_message_box_new(Message.c_str(), err?GNOME_MESSAGE_BOX_ERROR:GNOME_MESSAGE_BOX_INFO, GNOME_STOCK_BUTTON_CLOSE, 0); gnome_dialog_set_close(GNOME_DIALOG(Window), true); #else Window = gtk_message_dialog_new ((GtkWindow*)0, GTK_DIALOG_DESTROY_WITH_PARENT, err?GTK_MESSAGE_ERROR:GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, Message.c_str()); g_signal_connect_swapped (GTK_OBJECT (Window), "response", G_CALLBACK (gtk_widget_destroy), GTK_OBJECT (Window)); #endif g_signal_connect (G_OBJECT(Window), "destroy", G_CALLBACK(gtk_widget_destroy), Window); g_signal_connect (G_OBJECT(Window), "delete_event", G_CALLBACK(gtk_widget_destroy), Window); gtk_window_set_position(GTK_WINDOW(Window), GTK_WIN_POS_MOUSE); gtk_widget_show(Window); } /// @return true if a file or directory specified by Path exists bool fr_Exists(const std::string& Path) { struct stat StatBuf; if(Path.size() < 1) return 0; return (stat(Path.c_str(), &StatBuf)==0); } /// @return true if Dir is an existing directory bool fr_DirExists(const std::string& Dir) { struct stat StatBuf; if(Dir.size()<1) return 0; if(stat(Dir.c_str(), &StatBuf)) return false; return (StatBuf.st_mode & S_IFDIR); } /// @return true if Filename is an existing file bool fr_FileExists(const std::string& Filename) { struct stat StatBuf; if(Filename.size() < 1) return false; if(stat(Filename.c_str(), &StatBuf)) return false; return (StatBuf.st_mode & S_IFREG); } /// return a string with the name of the Home Directory std::string fr_HomeDir() { return g_get_home_dir(); } /// return the absolute path name for a possibly relative path std::string fr_RealPath(const std::string& p) { char path[PATH_MAX]; if(!realpath(p.c_str(), path)) return p; path[PATH_MAX-1] = 0; return path; } /// return a string with the directory name taken from the string p std::string fr_Dirname(const std::string& p) { if(p.size()==0) return p; return g_dirname(p.c_str()); } /// return a string with the basename taken from the string p std::string fr_Basename(const std::string& p) { if(p.size()==0) return p; return g_basename(p.c_str()); } /// return a string with the file extension used in p /// @param all if true, return the full extension (ie .tar.gz) /// if false, return the last extension (ie .gz) std::string fr_FileExt(const std::string& p, bool all) { std::string f = fr_Basename(p); unsigned int dot; if(!f.size()) dot = f.npos; else if(all) dot = f.find('.', 1); else dot = f.rfind('.'); if((dot==0) || (dot==f.npos)) return ""; return f.substr(dot); } /// escape a string to be suitable as a command shell argument. std::string fr_EscapeArg(const std::string& a) { const static char*esc1 = " `~!#$&*()[]{}<>?|\\'\";"; const static char*esc2 = "!$\\\""; const char *esc = esc1; char c, q = 0; int danger = 0; std::string s; std::string::const_iterator i = a.begin(), e = a.end(); for(; i!=e; i++) { c = *i; if(strchr(esc,c)) danger++; else if(iscntrl(c)) { danger = 2; break; } } if(!danger) return a; if(danger > 1) { q = '"'; esc = esc2; } if(q) s += q; for(i=a.begin(); i!=e; i++) { c = *i; if(strchr(esc, c)) s += '\\'; s += c; } if(q) s += q; return s; } std::string fr_UrlEncode(const std::string& s) { std::string u; char c, hex[5]; if(!s.size()) return s; std::string::const_iterator i = s.begin(), e = s.end(); for(; i!=e; i++) { c = *i; if(isalnum(c) || strchr("_-/.,", c)) u += c; else if(c==' ') u += '+'; else { snprintf(hex, sizeof(hex), "%%%.2X", (int)c); u += hex; } } return u; } std::string fr_UrlEncode(const strmap& sm) { std::string u, key, val; strmap::const_iterator b = sm.begin(), e = sm.end(), i; for(i=b; i!=e; i++) { key = i->first; val = i->second; if(!key.size() || !val.size()) continue; if(u.size()) u += '&'; u += fr_UrlEncode(key) + '=' + fr_UrlEncode(val); } return u; } void fr_UrlDecode(const std::string& s, strmap& sm) { std::string key, buf; char c, hex[3]; if(!s.size()) return; memset(hex, 0, sizeof(hex)); std::string::const_iterator i = s.begin(), e = s.end(); do { c = *i++; switch(c) { case '%': if(i != e) hex[0] = *i++; if(i != e) hex[1] = *i++; if(i != e) buf += (char)(strtol(hex, NULL, 0x10)); break; case '+': buf += ' '; break; case '=': if(key.size()) buf += c; else { key = buf; buf = ""; } break; case '&': if(key.size()) sm[key] = buf; key = ""; buf = ""; break; default: buf += c; } } while(i != e); if(key.size()) sm[key] = buf; } /// do any unfinished GUI business void fr_Flush() { while(gtk_events_pending()) gtk_main_iteration(); } /// Initialize the GUI for this application void fr_init(const char*AppName, int argc, char*argv[], fr_ArgList& args) { int i; if(!AppName) AppName = g_basename(argv[0]); fr_AppName = AppName; #ifdef FR_GNOME gnome_init(AppName, 0, 1, argv); #else gtk_init(&argc, &argv); #endif for(i=1; i!=argc; i++) args << argv[i]; } int fr_exec(fr_ArgList& ExecArgs) { char **Args = ExecArgs.GetArgs(); if(Args[0]) { execvp(Args[0], Args); std::cerr << "execvp(\"" << Args[0] << "\", ...):\n" << fr_syserr() << std::endl; } else std::cerr << "fr_exec: No executable has been specified!; aborting" << std::endl; return -1; } std::string fr_syserr() { return strerror(errno); }