00001
00008
00009
00010 #include "NMEGtk.h"
00011 #include <stdio.h>
00012 #include <stdlib.h>
00013 #include <string.h>
00014
00015 static GdkCursor *textCursor = NULL;
00016 static GdkCursor *linkCursor = NULL;
00017
00018 void NMEGtkInit(NMEGtk *nmegtk, GtkTextBuffer *textBuffer,
00019 int charSize)
00020 {
00021 int i;
00022
00023 nmegtk->textBuffer = textBuffer;
00024 nmegtk->linkCB = NULL;
00025
00026 nmegtk->plainTag = gtk_text_buffer_create_tag(textBuffer, "plain", NULL);
00027 g_object_set(G_OBJECT(nmegtk->plainTag), "font", "serif", NULL);
00028 nmegtk->boldTag = gtk_text_buffer_create_tag(textBuffer, "bold", NULL);
00029 g_object_set(G_OBJECT(nmegtk->boldTag), "weight", PANGO_WEIGHT_BOLD, NULL);
00030 nmegtk->italicTag = gtk_text_buffer_create_tag(textBuffer, "italic", NULL);
00031 g_object_set(G_OBJECT(nmegtk->italicTag), "style", PANGO_STYLE_ITALIC, NULL);
00032 nmegtk->underlineTag = gtk_text_buffer_create_tag(textBuffer, "underline", NULL);
00033 g_object_set(G_OBJECT(nmegtk->underlineTag), "underline", PANGO_UNDERLINE_SINGLE, NULL);
00034 nmegtk->superTag = gtk_text_buffer_create_tag(textBuffer, "superscript", NULL);
00035 g_object_set(G_OBJECT(nmegtk->superTag), "rise", charSize / 2, NULL);
00036 nmegtk->subTag = gtk_text_buffer_create_tag(textBuffer, "subscript", NULL);
00037 g_object_set(G_OBJECT(nmegtk->subTag), "rise", -charSize / 3, NULL);
00038 nmegtk->monoTag = gtk_text_buffer_create_tag(textBuffer, "monospace", NULL);
00039 g_object_set(G_OBJECT(nmegtk->monoTag), "font", "monospace", NULL);
00040 nmegtk->headingTag[0] = gtk_text_buffer_create_tag(textBuffer, "h1", NULL);
00041 g_object_set(G_OBJECT(nmegtk->headingTag[0]),
00042 "weight", PANGO_WEIGHT_BOLD,
00043 "scale", PANGO_SCALE_XX_LARGE,
00044 "pixels-above-lines", charSize / PANGO_SCALE,
00045 "justification", GTK_JUSTIFY_CENTER,
00046 NULL);
00047 nmegtk->headingTag[1] = gtk_text_buffer_create_tag(textBuffer, "h2", NULL);
00048 g_object_set(G_OBJECT(nmegtk->headingTag[1]),
00049 "weight", PANGO_WEIGHT_BOLD,
00050 "scale", PANGO_SCALE_X_LARGE,
00051 "pixels-above-lines", 2 * charSize / 3 / PANGO_SCALE,
00052 NULL);
00053 nmegtk->headingTag[2] = gtk_text_buffer_create_tag(textBuffer, "h3", NULL);
00054 g_object_set(G_OBJECT(nmegtk->headingTag[2]),
00055 "weight", PANGO_WEIGHT_BOLD,
00056 "scale", PANGO_SCALE_LARGE,
00057 "pixels-above-lines", charSize / 2 / PANGO_SCALE,
00058 NULL);
00059 nmegtk->headingTag[3] = gtk_text_buffer_create_tag(textBuffer, "h4", NULL);
00060 g_object_set(G_OBJECT(nmegtk->headingTag[3]), "weight", PANGO_WEIGHT_BOLD,
00061 "pixels-above-lines", charSize / 3 / PANGO_SCALE,
00062 NULL);
00063 nmegtk->parTag = gtk_text_buffer_create_tag(textBuffer, "plainpar", NULL);
00064 g_object_set(G_OBJECT(nmegtk->parTag), "indent", 2 * charSize / PANGO_SCALE, NULL);
00065 for (i = 0; i < kMaxListLevel; i++)
00066 {
00067 char name[16];
00068
00069 sprintf(name, "indent%d", i + 1);
00070 nmegtk->indentTag[i] = gtk_text_buffer_create_tag(textBuffer, name, NULL);
00071 g_object_set(G_OBJECT(nmegtk->indentTag[i]),
00072 "left-margin", (3 * i + 1) * charSize / PANGO_SCALE,
00073 "indent", -charSize / PANGO_SCALE,
00074 NULL);
00075 }
00076
00077
00078 if (!linkCursor)
00079 linkCursor = gdk_cursor_new(GDK_LEFT_PTR);
00080 if (!textCursor)
00081 textCursor = gdk_cursor_new(GDK_XTERM);
00082 }
00083
00084 void NMEGtkSetLinkFun(NMEGtk *nmegtk, NMEGtkLinkFun linkFun, void *linkFunData)
00085 {
00086 nmegtk->linkCB = g_malloc(sizeof(NMEGtkLinkCB));
00087 nmegtk->linkCB->fun = linkFun;
00088 nmegtk->linkCB->data = linkFunData;
00089 g_object_set_data_full(G_OBJECT(nmegtk->textBuffer), "link_cb",
00090 nmegtk->linkCB, g_free);
00091 }
00092
00100 static gboolean linkEvent(GtkTextTag *tag, GtkWidget *w,
00101 GdkEventButton *event, GtkTextIter *iter,
00102 gpointer d)
00103 {
00104 NMEGtkLinkCB *l = (NMEGtkLinkCB *)d;
00105
00106 switch (event->type)
00107 {
00108 case GDK_BUTTON_RELEASE:
00109 if (l)
00110 {
00111 l->fun(g_object_get_data(G_OBJECT(tag), "link_url"),
00112 l->data);
00113 return TRUE;
00114 }
00115 break;
00116
00117 case GDK_ENTER_NOTIFY:
00118 if (linkCursor)
00119 gdk_window_set_cursor(gtk_widget_get_parent_window(w), linkCursor);
00120 return TRUE;
00121 case GDK_LEAVE_NOTIFY:
00122 if (textCursor)
00123 gdk_window_set_cursor(gtk_widget_get_parent_window(w), textCursor);
00124 return TRUE;
00125 }
00126
00127 return TRUE;
00128 }
00129
00130 void NMEGtkApplyStyle(NMEGtk const *nmegtk,
00131 NMEStyleTable const *spanTable,
00132 NMEInt offset, NMEInt length,
00133 NMEConstText nmeTextForLinks)
00134 {
00135 GtkTextIter start, end;
00136 GtkTextTag *tag;
00137 int i;
00138
00139 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &start,
00140 offset);
00141 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &end,
00142 offset + length);
00143 gtk_text_buffer_apply_tag(nmegtk->textBuffer, nmegtk->plainTag, &start, &end);
00144
00145 for (i = 0; i < spanTable->n; i++)
00146 {
00147 switch (spanTable->span[i].style)
00148 {
00149 case kNMEStyleCharBold:
00150 case kNMEStyleCharDT:
00151 case kNMEStyleCharTH:
00152 tag = nmegtk->boldTag;
00153 break;
00154 case kNMEStyleCharItalic:
00155 tag = nmegtk->italicTag;
00156 break;
00157 case kNMEStyleCharUnderline:
00158 tag = nmegtk->underlineTag;
00159 break;
00160 case kNMEStyleCharSuperscript:
00161 tag = nmegtk->superTag;
00162 break;
00163 case kNMEStyleCharSubscript:
00164 tag = nmegtk->subTag;
00165 break;
00166 case kNMEStyleCharMonospace:
00167 tag = nmegtk->monoTag;
00168 break;
00169 case kNMEStyleParHeading:
00170 if (spanTable->span[i].level <= kMaxHeadingLevel)
00171 tag = nmegtk->headingTag[spanTable->span[i].level - 1];
00172 else
00173 tag = nmegtk->headingTag[kMaxHeadingLevel - 1];
00174 break;
00175 case kNMEStyleParPlain:
00176 tag = nmegtk->parTag;
00177 break;
00178 case kNMEStyleParUL:
00179 case kNMEStyleParOL:
00180 case kNMEStyleParDL:
00181 case kNMEStyleParDT:
00182 case kNMEStyleParIndentedPar:
00183 if (spanTable->span[i].level <= kMaxListLevel)
00184 tag = nmegtk->indentTag[spanTable->span[i].level - 1];
00185 else
00186 tag = nmegtk->indentTag[kMaxHeadingLevel - 1];
00187 break;
00188 case kNMEStyleCharLink:
00189 if (nmeTextForLinks)
00190 {
00191 tag = gtk_text_buffer_create_tag(nmegtk->textBuffer, NULL,
00192 "foreground", "blue",
00193 "underline", PANGO_UNDERLINE_SINGLE,
00194 NULL);
00195 g_object_set_data_full(G_OBJECT(tag),
00196 "link_url",
00197 g_strndup(nmeTextForLinks + spanTable->span[i].linkOffset,
00198 spanTable->span[i].linkLength),
00199 g_free);
00200 g_signal_connect(G_OBJECT(tag), "event",
00201 G_CALLBACK(linkEvent), (gpointer)nmegtk->linkCB);
00202 break;
00203 }
00204
00205 default:
00206 tag = NULL;
00207 break;
00208 }
00209
00210 if (tag)
00211 {
00212 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &start,
00213 spanTable->span[i].begin + offset);
00214 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &end,
00215 spanTable->span[i].end + offset);
00216 gtk_text_buffer_apply_tag(nmegtk->textBuffer, tag, &start, &end);
00217 }
00218 }
00219 }
00220
00221 NMEErr NMEGtkInsert(GtkTextBuffer *textBuffer,
00222 NMEGtk const *nmegtk,
00223 NMEConstText str, NMEInt len,
00224 NMEBoolean replaceSel,
00225 NMEBoolean links)
00226 {
00227 NMEText buf, dest;
00228 NMEInt bufSize, destLen, destLenUCS16;
00229 NMEOutputFormat f;
00230 NMEErr err;
00231 int length;
00232 GtkTextIter iter;
00233
00234 if (len < 0)
00235 len = strlen(str);
00236
00237 bufSize = 1024 + 2 * len;
00238 f = NMEOutputFormatBasicText;
00239 f.spanHookFun = NMEStyleSpanHook;
00240 f.parHookFun = NMEStyleSpanHook;
00241
00242 tryAgain:
00243 buf = malloc(bufSize);
00244 if (!buf)
00245 return kNMEErrNotEnoughMemory;
00246 f.hookData = malloc(bufSize);
00247 if (!f.hookData)
00248 {
00249 free((void *)buf);
00250 return kNMEErrNotEnoughMemory;
00251 }
00252 NMEStyleInit((NMEStyleTable *)f.hookData, bufSize, TRUE);
00253
00254 err = NMEProcess(str, len, buf, bufSize,
00255 kNMEProcessOptDefault, "\n", &f, 0,
00256 &dest, &destLen, &destLenUCS16);
00257 if (err == kNMEErrNotEnoughMemory || err == kNMEErrStyleTableTooSmall)
00258 {
00259 free((void *)buf);
00260 free((void *)f.hookData);
00261 if (bufSize < 65536 + 10 * len)
00262 {
00263 bufSize *= 2;
00264 goto tryAgain;
00265 }
00266 else
00267 return kNMEErrNotEnoughMemory;
00268 }
00269
00270 if (replaceSel)
00271 {
00272 length = gtk_text_buffer_get_char_count(textBuffer);
00273 gtk_text_buffer_get_iter_at_offset(textBuffer, &iter, length);
00274 gtk_text_buffer_insert(textBuffer, &iter, dest, destLen);
00275 NMEGtkApplyStyle(nmegtk, (NMEStyleTable *)f.hookData,
00276 length, destLenUCS16, links ? str : NULL);
00277 }
00278 else
00279 {
00280 gtk_text_buffer_set_text(textBuffer, dest, destLen);
00281 NMEGtkApplyStyle(nmegtk, (NMEStyleTable *)f.hookData,
00282 0, destLenUCS16, links ? str : NULL);
00283 }
00284
00285 free((void *)buf);
00286 free((void *)f.hookData);
00287
00288 return kNMEErrOk;
00289 }