gmrun-main.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. /*
  2. * Copyright 2020 Mihai Bazon
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software
  5. * for any purpose with or without fee is hereby granted, provided that
  6. * the above copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
  10. * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
  11. * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR
  12. * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  13. * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  14. * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. *
  16. */
  17. #include <X11/Xlib.h>
  18. #include <gtk/gtk.h>
  19. #include <gdk/gdkkeysyms.h>
  20. #include <gdk/gdkx.h>
  21. #ifdef MTRACE
  22. #include <mcheck.h>
  23. #endif
  24. #include <unistd.h>
  25. #include <errno.h>
  26. #include "gtkcompletionline.h"
  27. #include "config_prefs.h"
  28. #define CMD_LENGTH 1024
  29. enum
  30. {
  31. W_TEXT_STYLE_NORMAL,
  32. W_TEXT_STYLE_NOTFOUND,
  33. W_TEXT_STYLE_NOTUNIQUE,
  34. W_TEXT_STYLE_UNIQUE,
  35. };
  36. GtkApplication * gmrun_app;
  37. char * gmrun_text = NULL;
  38. static void gmrun_exit (void);
  39. GtkAllocation window_geom = { -1, -1, -1, -1 };
  40. /* widgets that are used in several functions */
  41. GtkWidget * compline;
  42. GtkWidget * wlabel;
  43. GtkWidget * wlabel_search;
  44. /* preferences */
  45. int USE_GLIB_XDG = 0;
  46. int SHELL_RUN = 1;
  47. /// BEGIN: TIMEOUT MANAGEMENT
  48. static gboolean search_off_timeout ();
  49. static guint g_search_off_timeout_id = 0;
  50. static void remove_search_off_timeout (void)
  51. {
  52. if (g_search_off_timeout_id) {
  53. g_source_remove(g_search_off_timeout_id);
  54. g_search_off_timeout_id = 0;
  55. }
  56. }
  57. static void add_search_off_timeout (guint32 timeout, GSourceFunc func)
  58. {
  59. remove_search_off_timeout();
  60. if (!func)
  61. func = (GSourceFunc) search_off_timeout;
  62. g_search_off_timeout_id = g_timeout_add (timeout, func, NULL);
  63. }
  64. /// END: TIMEOUT MANAGEMENT
  65. // https://unix.stackexchange.com/questions/457584/gtk3-change-text-color-in-a-label-raspberry-pi
  66. static void set_info_text_color (GtkWidget *w, const char *text, int spec)
  67. {
  68. char *markup = NULL;
  69. static const char * colors[] = {
  70. "black", /* W_TEXT_STYLE_NORMAL */
  71. "red", /* W_TEXT_STYLE_NOTFOUND */
  72. "blue", /* W_TEXT_STYLE_NOTUNIQUE */
  73. "green", /* W_TEXT_STYLE_UNIQUE */
  74. };
  75. markup = g_markup_printf_escaped ("<span foreground=\"%s\">%s</span>",
  76. colors[spec], text);
  77. if (markup) {
  78. gtk_label_set_markup (GTK_LABEL (w), markup);
  79. g_free(markup);
  80. }
  81. }
  82. static void run_the_command (char * cmd)
  83. {
  84. #if DEBUG
  85. fprintf (stderr, "command: %s\n", cmd);
  86. #endif
  87. if (SHELL_RUN)
  88. {
  89. // need to add extra &
  90. if (strlen (cmd) < (CMD_LENGTH-10)) {
  91. strcat (cmd, " &"); /* safe to use in this case */
  92. }
  93. int ret = system (cmd);
  94. if (ret != -1) {
  95. gmrun_exit ();
  96. } else {
  97. char errmsg[256];
  98. snprintf (errmsg, sizeof(errmsg)-1, "ERROR: %s", strerror (errno));
  99. set_info_text_color (wlabel, errmsg, W_TEXT_STYLE_NOTFOUND);
  100. add_search_off_timeout (3000, NULL);
  101. }
  102. }
  103. else // glib - more conservative approach and robust error reporting
  104. {
  105. GError * error = NULL;
  106. gboolean success;
  107. int argc;
  108. char ** argv;
  109. success = g_shell_parse_argv (cmd, &argc, &argv, &error);
  110. if (!success) {
  111. set_info_text_color (wlabel, error->message, W_TEXT_STYLE_NOTFOUND);
  112. g_error_free (error);
  113. add_search_off_timeout (3000, NULL);
  114. return;
  115. }
  116. success = g_spawn_async (NULL, argv, NULL,
  117. G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
  118. if (argv) {
  119. g_strfreev (argv);
  120. }
  121. if (success) {
  122. gmrun_exit ();
  123. } else {
  124. set_info_text_color (wlabel, error->message, W_TEXT_STYLE_NOTFOUND);
  125. g_error_free (error);
  126. add_search_off_timeout (3000, NULL);
  127. }
  128. }
  129. }
  130. static void
  131. on_ext_handler (GtkCompletionLine *cl, const char * filename)
  132. {
  133. if (USE_GLIB_XDG) // GLib XDG handling (freedesktop specification)
  134. {
  135. gchar * content_type, * mime_type, * msg;
  136. const gchar * handler;
  137. GAppInfo * app_info;
  138. if (filename) {
  139. content_type = g_content_type_guess (filename, NULL, 0, NULL);
  140. if (content_type) {
  141. mime_type = g_content_type_get_mime_type (content_type);
  142. g_free (content_type);
  143. app_info = g_app_info_get_default_for_type (mime_type, FALSE);
  144. g_free (mime_type);
  145. if (app_info) {
  146. handler = g_app_info_get_commandline (app_info);
  147. msg = g_strconcat("Handler: ", handler, NULL);
  148. gtk_label_set_text (GTK_LABEL (wlabel_search), msg);
  149. gtk_widget_show (wlabel_search);
  150. g_object_unref(app_info);
  151. g_free(msg);
  152. return;
  153. }
  154. }
  155. }
  156. search_off_timeout();
  157. }
  158. else // custom EXT handlers
  159. {
  160. const char * ext = strrchr (filename, '.');
  161. if (!ext) {
  162. search_off_timeout ();
  163. return;
  164. }
  165. const char * handler = config_get_handler_for_extension (ext);
  166. if (handler) {
  167. char * tmp = g_strconcat ("Handler: ", handler, NULL);
  168. gtk_label_set_text (GTK_LABEL (wlabel_search), tmp);
  169. gtk_widget_show (wlabel_search);
  170. g_free (tmp);
  171. }
  172. }
  173. }
  174. static void on_compline_runwithterm (GtkCompletionLine *cl)
  175. {
  176. char cmd[CMD_LENGTH];
  177. char * term;
  178. char * entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY(cl)));
  179. g_strstrip (entry_text);
  180. if (*entry_text) {
  181. if (config_get_string_expanded ("TermExec", &term)) {
  182. snprintf (cmd, sizeof (cmd), "%s %s", term, entry_text);
  183. g_free (term);
  184. } else {
  185. snprintf (cmd, sizeof (cmd), "xterm -e %s", entry_text);
  186. }
  187. } else {
  188. if (config_get_string ("Terminal", &term)) {
  189. strncpy (cmd, term, sizeof (cmd) - 1);
  190. } else {
  191. strncpy (cmd, "xterm", sizeof (cmd) - 1);
  192. }
  193. }
  194. history_append (cl->hist, cmd);
  195. run_the_command (cmd);
  196. g_free (entry_text);
  197. }
  198. static gboolean search_off_timeout ()
  199. {
  200. set_info_text_color (wlabel, "Program çalıştır:", W_TEXT_STYLE_NORMAL);
  201. gtk_widget_hide (wlabel_search);
  202. g_search_off_timeout_id = 0;
  203. return G_SOURCE_REMOVE;
  204. }
  205. static void
  206. on_compline_unique (GtkCompletionLine *cl)
  207. {
  208. set_info_text_color (wlabel, "benzersiz", W_TEXT_STYLE_UNIQUE);
  209. add_search_off_timeout (1000, NULL);
  210. }
  211. static void
  212. on_compline_notunique (GtkCompletionLine *cl)
  213. {
  214. set_info_text_color (wlabel, "benzersiz değil", W_TEXT_STYLE_NOTUNIQUE);
  215. add_search_off_timeout (1000, NULL);
  216. }
  217. static void
  218. on_compline_incomplete (GtkCompletionLine *cl)
  219. {
  220. set_info_text_color (wlabel, "bulunamadı", W_TEXT_STYLE_NOTFOUND);
  221. add_search_off_timeout (1000, NULL);
  222. }
  223. static void
  224. on_search_mode (GtkCompletionLine *cl)
  225. {
  226. if (cl->hist_search_mode == TRUE) {
  227. gtk_widget_show (wlabel_search);
  228. gtk_label_set_text (GTK_LABEL (wlabel), "Ara:");
  229. gtk_label_set_text (GTK_LABEL (wlabel_search), cl->hist_word);
  230. } else {
  231. gtk_widget_hide (wlabel_search);
  232. gtk_label_set_text (GTK_LABEL (wlabel), "Arama Kapalı");
  233. add_search_off_timeout (1000, NULL);
  234. }
  235. }
  236. static void
  237. on_search_letter(GtkCompletionLine *cl, GtkWidget *label)
  238. {
  239. gtk_label_set_text (GTK_LABEL(label), cl->hist_word);
  240. }
  241. static gboolean search_fail_timeout (gpointer user_data)
  242. {
  243. set_info_text_color (wlabel, "Ara:", W_TEXT_STYLE_NOTUNIQUE);
  244. g_search_off_timeout_id = 0;
  245. return G_SOURCE_REMOVE;
  246. }
  247. static void
  248. on_search_not_found(GtkCompletionLine *cl)
  249. {
  250. set_info_text_color (wlabel, "Bulunamadı!", W_TEXT_STYLE_NOTFOUND);
  251. add_search_off_timeout (1000, (GSourceFunc) search_fail_timeout);
  252. }
  253. // =============================================================
  254. static void xdg_app_run_command (GAppInfo *app, const gchar *args)
  255. {
  256. // get
  257. char * cmd, * exe;
  258. GRegex * regex;
  259. regex = g_regex_new (".%[fFuUdDnNickvm]", G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY, NULL);
  260. exe = g_regex_replace_literal (regex, // Remove xdg desktop files fields from app
  261. g_app_info_get_commandline (app),
  262. -1, 0, "", G_REGEX_MATCH_NOTEMPTY, NULL);
  263. cmd = g_strconcat (exe, " ", args, NULL);
  264. run_the_command (cmd); /* Launch the command */
  265. g_regex_unref (regex);
  266. g_free (exe);
  267. g_free (cmd);
  268. }
  269. /* Handler for URLs */
  270. static gboolean url_check (GtkCompletionLine *cl, char * entry_text)
  271. {
  272. if (USE_GLIB_XDG) // GLib XDG handling (freedesktop specification)
  273. {
  274. char * delim;
  275. const char * url, * protocol;
  276. GAppInfo * app;
  277. delim = strchr (entry_text, ':');
  278. if (!delim || !*(delim+1)) {
  279. return FALSE;
  280. }
  281. protocol = entry_text;
  282. url = delim + 1;
  283. *delim = 0;
  284. if (url[0] == '/' && url[1] == '/')
  285. {
  286. app = g_app_info_get_default_for_uri_scheme (protocol);
  287. if (app) { // found known uri handler for protocol
  288. *delim = ':';
  289. xdg_app_run_command (app, entry_text);
  290. history_append (cl->hist, entry_text);
  291. g_object_unref (app);
  292. } else {
  293. char *tmp = g_strconcat ("No URL handler for [", protocol, "]", NULL);
  294. set_info_text_color (wlabel, tmp, W_TEXT_STYLE_NOTFOUND);
  295. add_search_off_timeout (1000, NULL);
  296. g_free (tmp);
  297. }
  298. return TRUE;
  299. }
  300. *delim = ':';
  301. return FALSE;
  302. }
  303. else //-------- custom URL handlers
  304. {
  305. // <url_type> <delim> <url>
  306. // http : //www.fsf.org
  307. // <f u l l u r l>
  308. // config: URL_<url_type>
  309. // handler %s (format 1) = run handler with <url>
  310. // handler %u (format 2) = run handler with <full url>
  311. char * cmd;
  312. char * tmp, * delim, * p;
  313. char * url, * url_type, * full_url, * chosen_url;
  314. char * url_handler;
  315. char * config_key;
  316. cmd = tmp = delim = p = url_handler = config_key = NULL;
  317. delim = strchr (entry_text, ':');
  318. if (!delim || !*(delim+1)) {
  319. return FALSE;
  320. }
  321. tmp = g_strdup (entry_text);
  322. delim = strchr (tmp, ':');
  323. *delim = 0;
  324. url_type = tmp; // http
  325. url = delim + 1; // //www.fsf.org
  326. full_url = entry_text;
  327. config_key = g_strconcat ("URL_", url_type, NULL);
  328. if (config_get_string_expanded (config_key, &url_handler))
  329. {
  330. chosen_url = url;
  331. p = strchr (url_handler, '%');
  332. if (p) { // handler %s
  333. p++;
  334. if (*p == 'u') { // handler %u
  335. *p = 's'; // convert %u to %s (for printf)
  336. chosen_url = full_url;
  337. }
  338. cmd = g_strdup_printf (url_handler, chosen_url);
  339. } else {
  340. cmd = g_strconcat (url_handler, " ", url, NULL);
  341. }
  342. g_free (url_handler);
  343. }
  344. if (cmd) {
  345. history_append (cl->hist, entry_text);
  346. run_the_command (cmd);
  347. g_free (cmd);
  348. } else {
  349. g_free (tmp);
  350. tmp = g_strconcat (" [", config_key, "] için URL işleyici yok", NULL);
  351. set_info_text_color (wlabel, tmp, W_TEXT_STYLE_NOTFOUND);
  352. add_search_off_timeout (1000, NULL);
  353. }
  354. g_free (config_key);
  355. g_free (tmp);
  356. return TRUE;
  357. }
  358. }
  359. static char * escape_spaces (char * entry_text)
  360. { // run file with glib: replace " " with "\ "
  361. GRegex * regex;
  362. char * quoted;
  363. if (!strstr (entry_text, "\\ ")) {
  364. regex = g_regex_new (" ", G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY, NULL);
  365. quoted = g_regex_replace_literal (regex, entry_text, -1, 0, "\\ ", G_REGEX_MATCH_NOTEMPTY, NULL);
  366. g_regex_unref (regex);
  367. } else {
  368. quoted = strdup (entry_text); // already scaped, just duplicate text
  369. }
  370. return (quoted);
  371. }
  372. /* Handler for extensions */
  373. static gboolean ext_check (GtkCompletionLine *cl, char * entry_text)
  374. {
  375. if (USE_GLIB_XDG) // GLib XDG handling (freedesktop specification)
  376. {
  377. char *quoted, *content_type, *mime_type;
  378. GAppInfo *app_info;
  379. gboolean sure;
  380. quoted = escape_spaces (entry_text);
  381. /* File is executable: launch it (fail silently if file isn't really executable) */
  382. if (g_file_test (quoted, G_FILE_TEST_IS_EXECUTABLE)) {
  383. run_the_command (quoted);
  384. history_append (cl->hist, entry_text);
  385. g_free (quoted);
  386. return TRUE;
  387. }
  388. /* Check mime type through extension */
  389. if (quoted[0] == '/' && strchr (quoted, '.')) {
  390. content_type = g_content_type_guess (quoted, NULL, 0, &sure);
  391. if (content_type) {
  392. mime_type = g_content_type_get_mime_type (content_type);
  393. g_free (content_type);
  394. app_info = g_app_info_get_default_for_type (mime_type, FALSE);
  395. g_free (mime_type);
  396. if (app_info) { // found mime
  397. xdg_app_run_command (app_info, quoted);
  398. history_append (cl->hist, entry_text);
  399. g_free (quoted);
  400. g_object_unref(app_info);
  401. return TRUE;
  402. }
  403. }
  404. }
  405. g_free (quoted);
  406. return FALSE;
  407. }
  408. else //-------- custom EXTension handlers
  409. {
  410. // example: file.html | xdg-open '%s' -> xdg-open 'file.html'
  411. char * cmd, * quoted;
  412. char * ext = strrchr (entry_text, '.');
  413. char * handler_format = NULL;
  414. if (ext) {
  415. handler_format = config_get_handler_for_extension (ext);
  416. }
  417. if (handler_format) {
  418. quoted = g_strcompress (entry_text); /* unescape chars */
  419. if (strstr (handler_format, "%s")) {
  420. cmd = g_strdup_printf (handler_format, quoted);
  421. }
  422. else { // xdg-open
  423. cmd = g_strconcat (handler_format, " '", quoted, "'", NULL);
  424. }
  425. history_append (cl->hist, entry_text);
  426. run_the_command (cmd);
  427. g_free (cmd);
  428. g_free (quoted);
  429. return TRUE;
  430. }
  431. return FALSE;
  432. }
  433. }
  434. // =============================================================
  435. static void on_compline_activated (GtkCompletionLine *cl)
  436. {
  437. char * entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY(cl)));
  438. g_strstrip (entry_text);
  439. if (url_check (cl, entry_text) == TRUE
  440. || ext_check (cl, entry_text) == TRUE) {
  441. g_free (entry_text);
  442. return;
  443. }
  444. char cmd[CMD_LENGTH];
  445. char * AlwaysInTerm = NULL;
  446. char ** term_progs = NULL;
  447. char * selected_term_prog = NULL;
  448. if (config_get_string ("AlwaysInTerm", &AlwaysInTerm))
  449. {
  450. term_progs = g_strsplit (AlwaysInTerm, " ", 0);
  451. int i;
  452. for (i = 0; term_progs[i]; i++) {
  453. if (strcmp (term_progs[i], entry_text) == 0) {
  454. selected_term_prog = g_strdup (term_progs[i]);
  455. break;
  456. }
  457. }
  458. g_strfreev (term_progs);
  459. }
  460. if (selected_term_prog) {
  461. char * TermExec;
  462. config_get_string_expanded ("TermExec", &TermExec);
  463. snprintf (cmd, sizeof (cmd), "%s %s", TermExec, selected_term_prog);
  464. g_free (selected_term_prog);
  465. g_free (TermExec);
  466. } else {
  467. strncpy (cmd, entry_text, sizeof (cmd) - 1);
  468. }
  469. g_free (entry_text);
  470. history_append (cl->hist, cmd);
  471. run_the_command (cmd);
  472. }
  473. // =============================================================
  474. static void gmrun_activate(void)
  475. {
  476. GtkWidget *dialog, * main_vbox;
  477. GtkWidget *label_search;
  478. GtkWidget * window = gtk_application_window_new (gmrun_app);
  479. dialog = gtk_dialog_new();
  480. gtk_window_set_transient_for( (GtkWindow*)dialog, (GtkWindow*)window );
  481. gtk_widget_realize(dialog);
  482. main_vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  483. // this removes the title bar..
  484. GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET(dialog));
  485. gdk_window_set_decorations (gwin, GDK_DECOR_BORDER);
  486. gtk_widget_set_name (GTK_WIDGET (dialog), "gmrun");
  487. gtk_window_set_title (GTK_WINDOW(window), "Tamamlanmış basit bir başlatıcı");
  488. gtk_container_set_border_width(GTK_CONTAINER(dialog), 4);
  489. g_signal_connect(G_OBJECT(dialog), "destroy",
  490. G_CALLBACK(gmrun_exit), NULL);
  491. GtkWidget *hhbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
  492. gtk_box_pack_start (GTK_BOX (main_vbox), hhbox, FALSE, FALSE, 0);
  493. GtkWidget *label = gtk_label_new("Program çalıştır:");
  494. gtk_box_pack_start (GTK_BOX(hhbox), label, FALSE, FALSE, 10);
  495. gtkcompat_widget_set_halign_left (GTK_WIDGET (label));
  496. wlabel = label;
  497. label_search = gtk_label_new("");
  498. gtk_box_pack_start (GTK_BOX (hhbox), label_search, FALSE, TRUE, 0);
  499. wlabel_search = label_search;
  500. compline = gtk_completion_line_new();
  501. gtk_widget_set_name (compline, "gmrun_compline");
  502. gtk_box_pack_start (GTK_BOX (main_vbox), compline, TRUE, TRUE, 0);
  503. if (!config_get_int ("SHELL_RUN", &SHELL_RUN)) {
  504. SHELL_RUN = 1;
  505. }
  506. // don't show files starting with "." by default
  507. if (!config_get_int ("ShowDotFiles", &(GTK_COMPLETION_LINE(compline)->show_dot_files))) {
  508. GTK_COMPLETION_LINE(compline)->show_dot_files = 0;
  509. }
  510. int tmp;
  511. if (!config_get_int ("TabTimeout", &tmp)) {
  512. ((GtkCompletionLine*)compline)->tabtimeout = tmp;
  513. }
  514. if (!config_get_int ("USE_GLIB_XDG", &USE_GLIB_XDG)) {
  515. USE_GLIB_XDG = 0;
  516. }
  517. g_signal_connect(G_OBJECT(compline), "cancel",
  518. G_CALLBACK(gmrun_exit), NULL);
  519. g_signal_connect(G_OBJECT(compline), "activate",
  520. G_CALLBACK (on_compline_activated), NULL);
  521. g_signal_connect(G_OBJECT(compline), "runwithterm",
  522. G_CALLBACK (on_compline_runwithterm), NULL);
  523. g_signal_connect(G_OBJECT(compline), "unique",
  524. G_CALLBACK (on_compline_unique), NULL);
  525. g_signal_connect(G_OBJECT(compline), "notunique",
  526. G_CALLBACK (on_compline_notunique), NULL);
  527. g_signal_connect(G_OBJECT(compline), "incomplete",
  528. G_CALLBACK (on_compline_incomplete), NULL);
  529. g_signal_connect(G_OBJECT(compline), "search_mode",
  530. G_CALLBACK (on_search_mode), NULL);
  531. g_signal_connect(G_OBJECT(compline), "search_not_found",
  532. G_CALLBACK (on_search_not_found), NULL);
  533. g_signal_connect(G_OBJECT(compline), "search_letter",
  534. G_CALLBACK(on_search_letter), label_search);
  535. g_signal_connect(G_OBJECT(compline), "ext_handler",
  536. G_CALLBACK (on_ext_handler), NULL);
  537. int shows_last_history_item;
  538. if (!config_get_int ("ShowLast", &shows_last_history_item)) {
  539. shows_last_history_item = 0;
  540. }
  541. if (gmrun_text) {
  542. gtk_entry_set_text (GTK_ENTRY(compline), gmrun_text);
  543. } else if (shows_last_history_item) {
  544. gtk_completion_line_last_history_item (GTK_COMPLETION_LINE(compline));
  545. }
  546. // geometry: window position
  547. if (window_geom.x > -1 || window_geom.y > -1) {
  548. gtk_window_move (GTK_WINDOW (dialog), window_geom.x, window_geom.y);
  549. } else {
  550. /* default: centered */
  551. gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ALWAYS);
  552. }
  553. // geometry: window size
  554. if (window_geom.height > -1 || window_geom.width > -1) {
  555. gtk_window_set_default_size (GTK_WINDOW (dialog), window_geom.width,
  556. window_geom.height);
  557. } else {
  558. /* default width = 450 */
  559. gtk_window_set_default_size (GTK_WINDOW (dialog), 450, -1);
  560. }
  561. // window icon
  562. GError * error = NULL;
  563. GtkIconTheme * theme = gtk_icon_theme_get_default ();
  564. GdkPixbuf * icon = gtk_icon_theme_load_icon (theme, "gmrun", 48, GTK_ICON_LOOKUP_USE_BUILTIN, &error);
  565. if (error) {
  566. g_object_set (dialog, "icon-name", "gtk-execute", NULL);
  567. g_error_free (error);
  568. } else {
  569. gtk_window_set_icon (GTK_WINDOW (dialog), icon);
  570. g_object_unref (icon);
  571. }
  572. gtk_widget_show_all (dialog);
  573. gtk_window_set_focus(GTK_WINDOW(dialog), compline);
  574. }
  575. // =============================================================
  576. static void parse_command_line (int argc, char ** argv)
  577. {
  578. // --geometry / parse commandline options
  579. static char *geometry_str = NULL;
  580. GError *error = NULL;
  581. GOptionContext *context = NULL;
  582. static GOptionEntry entries[] =
  583. {
  584. { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry_str, "Bu seçenek, pencerenin başlangıç ​​boyutunu ve konumunu belirtir.", NULL, },
  585. { NULL },
  586. };
  587. context = g_option_context_new (NULL);
  588. g_option_context_add_main_entries (context, entries, NULL);
  589. g_option_context_add_group (context, gtk_get_option_group (TRUE));
  590. if (!g_option_context_parse (context, &argc, &argv, &error))
  591. {
  592. g_print ("seçenek ayrıştırılamadı: %s\n", error->message);
  593. if (context) g_option_context_free (context);
  594. if (error) g_error_free (error);
  595. if (geometry_str) g_free (geometry_str);
  596. exit (1);
  597. }
  598. if (context) g_option_context_free (context);
  599. if (error) g_error_free (error);
  600. if (argc >= 2) {
  601. gmrun_text = argv[1];
  602. }
  603. // --
  604. if (!geometry_str)
  605. {
  606. // --geometry was not specified, see config file
  607. char * geomstr;
  608. if (config_get_string ("Geometry", &geomstr)) {
  609. geometry_str = g_strdup (geomstr);
  610. }
  611. }
  612. if (geometry_str)
  613. {
  614. // --geometry WxH+X+Y
  615. // width x height + posX + posY
  616. int width, height, posX, posY;
  617. char *Wstr, *Hstr, *Xstr, *Ystr;
  618. Wstr = Hstr = Xstr = Ystr = NULL;
  619. Xstr = strchr (geometry_str, '+');
  620. if (Xstr) { // +posX+posY
  621. *Xstr = 0;
  622. Xstr++; // posX+posY
  623. Ystr = strchr (Xstr, '+');
  624. if (Ystr) { // +posY
  625. *Ystr = 0;
  626. Ystr++; // posY
  627. }
  628. }
  629. if (Xstr && Ystr && *Xstr && *Ystr) {
  630. posX = strtoll (Xstr, NULL, 0);
  631. posY = strtoll (Ystr, NULL, 0);
  632. ///fprintf (stderr, "x: %" G_GINT64_FORMAT "\ny: %" G_GINT64_FORMAT "\n", posX, posY);
  633. window_geom.x = posX;
  634. window_geom.y = posY;
  635. }
  636. Hstr = strchr (geometry_str, 'x');
  637. if (Hstr) { // WxH
  638. *Hstr = 0;
  639. Hstr++; // H
  640. Wstr = geometry_str;
  641. width = strtoll (Wstr, NULL, 0);
  642. height = strtoll (Hstr, NULL, 0);
  643. ///fprintf (stderr, "w: %" G_GINT64_FORMAT "\nh: %" G_GINT64_FORMAT "\n", width, height);
  644. window_geom.width = width;
  645. window_geom.height = height;
  646. }
  647. g_free (geometry_str);
  648. }
  649. }
  650. // =============================================================
  651. // MAIN
  652. void gmrun_exit(void)
  653. {
  654. gtk_widget_destroy (compline);
  655. config_destroy ();
  656. g_application_quit (G_APPLICATION (gmrun_app));
  657. }
  658. int main(int argc, char **argv)
  659. {
  660. #ifdef MTRACE
  661. mtrace();
  662. #endif
  663. int status = 0;
  664. config_init ();
  665. parse_command_line (argc, argv);
  666. #if GTK_CHECK_VERSION(3, 4, 0)
  667. // Handling cmd line args with GApplication is a nightmare
  668. // follow this: https://developer.gnome.org/gtkmm-tutorial/stable/sec-multi-item-containers.html.en#boxes-command-line-options
  669. argc = 1; /* hide args from GApplication */
  670. gmrun_app = gtk_application_new ("org.gtk.gmrun", G_APPLICATION_NON_UNIQUE);
  671. g_signal_connect (gmrun_app, "activate", gmrun_activate, NULL);
  672. status = g_application_run (G_APPLICATION (gmrun_app), argc, argv);
  673. g_object_unref (gmrun_app);
  674. #else
  675. gtk_init (&argc, &argv);
  676. gmrun_activate ();
  677. gtk_main ();
  678. #endif
  679. #ifdef MTRACE
  680. muntrace();
  681. #endif
  682. return (status);
  683. }